Chapter 6 of 12 ~20 min read

Classes & Objects

Kotlin classes are more concise than Java — no boilerplate getters/setters, no-braces constructors, and smarter defaults.


Class Declaration

Java
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge()     { return age; }
    public void setAge(int age) { this.age = age; }
}
Kotlin
class Person(
    val name: String,  // immutable property
    var age: Int       // mutable property
)
// That's it! Constructor + properties in one line.

// Usage:
val p = Person("Alice", 30)
println(p.name)   // Alice
p.age = 31        // direct access — no setters!
Properties, not fields

Kotlin automatically generates getters for val and getters+setters for var. When calling from Java, these appear as getName() and setAge(). From Kotlin, you use direct dot access: p.name.

init Block

Kotlin
class Person(val name: String, var age: Int) {
    val displayName: String

    init {
        // Runs on construction — like Java constructor body
        displayName = name.trim().uppercase()
        require(age >= 0) { "Age must be non-negative" }
    }
}

Inheritance — open Required

In Java, all classes are open for inheritance by default. In Kotlin, classes and methods are final by default — you must explicitly mark them open:

Java
// Open by default!
public class Animal {
    public String speak() {
        return "...";
    }
}

public class Dog extends Animal {
    @Override
    public String speak() {
        return "Woof!";
    }
}
Kotlin
// Final by default!
open class Animal {
    open fun speak() = "..."
}

class Dog : Animal() {
    override fun speak() = "Woof!"
}

// Non-open class can't be extended:
class Cat  // final — cannot extend

companion object vs Java static

Kotlin has no static keyword. Use companion object for class-level members:

Java
public class ApiClient {
    private static final String BASE_URL =
        "https://api.example.com";
    private static ApiClient instance;

    public static ApiClient getInstance() {
        if (instance == null)
            instance = new ApiClient();
        return instance;
    }

    public static String getBaseUrl() {
        return BASE_URL;
    }
}
Kotlin
class ApiClient {
    companion object {
        const val BASE_URL = "https://api.example.com"
        
        // Can implement factory pattern:
        fun create() = ApiClient()
    }
}

// Call like Java static:
ApiClient.BASE_URL
ApiClient.create()

object — Singleton in One Keyword

Java — singleton boilerplate
public class Logger {
    private static Logger instance;
    private Logger() {}

    public static Logger getInstance() {
        if (instance == null)
            instance = new Logger();
        return instance;
    }

    public void log(String msg) {
        System.out.println("[LOG] " + msg);
    }
}
Kotlin — one keyword
object Logger {
    fun log(msg: String) {
        println("[LOG] $msg")
    }
}

// Usage — no getInstance() needed:
Logger.log("App started")