Table of contents
Class Declaration
Kotlin class declarations are much more concise than Java. The constructor can be written directly in the class header.
class User(val name: String, var age: Int)
This single line does the same job as the following Java code.
public class User {
private final String name;
private int age;
public User(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; }
}
Declaring with val generates only a getter; var generates both getter and setter. These are called properties — a concept that combines Java’s fields with getters/setters.
val user = User("Kim", 25)
println(user.name) // Calls the getter
user.age = 26 // Calls the setter
data class
One of the features you’ll use most in practice. When creating DTOs or value objects in Java, you had to write equals(), hashCode(), toString(), and copy() boilerplate manually or use Lombok. In Kotlin, the single word data handles it all.
data class Product(
val id: Long,
val name: String,
val price: Int
)
Declaring it this way automatically generates the following.
val p1 = Product(1, "Keyboard", 50000)
val p2 = Product(1, "Keyboard", 50000)
println(p1) // Product(id=1, name=Keyboard, price=50000)
println(p1 == p2) // true (content comparison)
println(p1.hashCode() == p2.hashCode()) // true
// copy — copy with only some fields changed
val p3 = p1.copy(price = 45000)
println(p3) // Product(id=1, name=Keyboard, price=45000)
// Destructuring
val (id, name, price) = p1
println("$name: $price won") // Keyboard: 50000 won
copy() is especially useful when working with immutable objects. You can specify only the fields you want to change without creating the entire object from scratch.
object — Singleton
In Java, creating a singleton requires a private constructor, a static instance, and thread safety considerations. In Kotlin, just use object.
object Database {
val url = "jdbc:mysql://localhost:3306/mydb"
fun connect() {
println("Connected to $url")
}
}
fun main() {
Database.connect() // Use directly without creating an instance
}
Declaring with object guarantees a singleton at the language level. You can access it directly without creating a separate instance.
companion object
This is the equivalent of Java’s static methods. Kotlin doesn’t have a static keyword; it uses companion object instead.
class User(val name: String) {
companion object {
fun create(name: String): User {
println("Creating user: $name")
return User(name)
}
}
}
fun main() {
val user = User.create("Kim") // Call directly on the class name
}
This is commonly used when implementing the factory method pattern.
Inheritance
Kotlin classes are final by default. To allow inheritance, you must explicitly mark them with open. This is an intentional design decision, aligned with “Effective Java“‘s recommendation to “prohibit inheritance for classes not designed for it.”
open class Animal(val name: String) {
open fun sound(): String = "..."
}
class Dog(name: String) : Animal(name) {
override fun sound(): String = "Woof"
}
class Cat(name: String) : Animal(name) {
override fun sound(): String = "Meow"
}
fun main() {
val animals = listOf(Dog("Buddy"), Cat("Whiskers"))
for (animal in animals) {
println("${animal.name}: ${animal.sound()}")
}
}
Both the class to inherit from and the methods to override need open. override is equivalent to Java’s @Override, but in Kotlin it’s a required keyword.
The inheritance hierarchy from the above example becomes clear when drawn as a class diagram.
classDiagram
class Animal {
+String name
+sound() String
}
class Dog {
+sound() String
}
class Cat {
+sound() String
}
Animal <|-- Dog
Animal <|-- Cat
note for Animal "open class<br/>open fun sound()"
Interfaces
Kotlin interfaces are similar to Java 8+ interfaces. They can have both abstract methods and default implementations.
interface Drawable {
fun draw() // Abstract method
fun description(): String { // Default implementation
return "Shape"
}
}
class Circle(val radius: Double) : Drawable {
override fun draw() {
println("Drawing a circle with radius $radius")
}
}
A class can only inherit from one class, but it can implement multiple interfaces. This principle is the same as in Java.
interface Clickable {
fun click()
}
interface Focusable {
fun focus()
}
class Button : Clickable, Focusable {
override fun click() = println("Clicked!")
override fun focus() = println("Focused!")
}
The multiple implementation relationship looks like this in a diagram. A class allows single inheritance, but multiple interfaces can be combined.
classDiagram
class Clickable {
<<interface>>
+click()
}
class Focusable {
<<interface>>
+focus()
}
class Button {
+click()
+focus()
}
Clickable <|.. Button
Focusable <|.. Button
The next part covers Kotlin collections and lambdas. We’ll look at functional APIs like filter, map, forEach, and scope functions (let, apply, run).




Loading comments...