Step 4 of 13
Classes, properties, inheritance, interfaces, data classes, sealed classes, enum classes
Classes, inheritance, interfaces, data classes, sealed classes, enum classes — ทุกอย่างพร้อมตัวอย่างโค้ด
class Person(val name: String, var age: Int)
fun main() {
val alice = Person("Alice", 30)
println(alice.name) // Alice
println(alice.age) // 30
alice.age = 31 // var allows reassignment
println(alice.age) // 31
}
The constructor parameters val name and var age become properties automatically. val creates a read-only property, var creates a mutable one.
When you need initialization logic, add a class body:
class BankAccount(val owner: String, initialBalance: Double) {
var balance: Double = initialBalance
private set // only this class can change balance
fun deposit(amount: Double) {
balance += amount
}
fun withdraw(amount: Double): Boolean {
return if (amount <= balance) {
balance -= amount
true
} else {
false
}
}
}
fun main() {
val account = BankAccount("Alice", 100.0)
account.deposit(50.0)
println(account.balance) // 150.0
val success = account.withdraw(200.0)
println(success) // false
println(account.balance) // 150.0
}
Kotlin classes are final by default. Use open to allow inheritance:
open class Animal(val name: String) {
open fun sound(): String {
return "Some sound"
}
}
class Dog(name: String) : Animal(name) {
override fun sound(): String {
return "Woof!"
}
}
class Cat(name: String) : Animal(name) {
override fun sound(): String {
return "Meow!"
}
}
fun main() {
val dog = Dog("Rex")
val cat = Cat("Whiskers")
println("${dog.name}: ${dog.sound()}") // Rex: Woof!
println("${cat.name}: ${cat.sound()}") // Whiskers: Meow!
}
Interfaces can have abstract methods and default implementations:
interface Greeter {
fun greet(): String
fun greetLoudly(): String {
return greet().uppercase()
}
}
class EnglishGreeter(val name: String) : Greeter {
override fun greet(): String {
return "Hello, $name"
}
}
fun main() {
val greeter = EnglishGreeter("Alice")
println(greeter.greet()) // Hello, Alice
println(greeter.greetLoudly()) // HELLO, ALICE
}
Use data class for objects that mainly hold data. Kotlin generates equals(), hashCode(), toString(), and copy() for free:
data class User(val name: String, val email: String, val age: Int)
fun main() {
val user1 = User("Alice", "alice@email.com", 30)
val user2 = User("Alice", "alice@email.com", 30)
println(user1) // User(name=Alice, email=alice@email.com, age=30)
println(user1 == user2) // true (structural equality)
// copy with changes
val olderUser = user1.copy(age = 31)
println(olderUser) // User(name=Alice, email=alice@email.com, age=31)
}
Sealed classes restrict a type to a fixed set of subclasses. They work great with when:
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
data object Loading : Result()
}
fun handleResult(result: Result): String = when (result) {
is Result.Success -> "Got data: ${result.data}"
is Result.Error -> "Failed: ${result.message}"
Result.Loading -> "Loading..."
}
fun main() {
println(handleResult(Result.Success("Hello"))) // Got data: Hello
println(handleResult(Result.Error("Timeout"))) // Failed: Timeout
println(handleResult(Result.Loading)) // Loading...
}
The when expression is exhaustive — the compiler checks that you handle every case.
enum class Priority(val level: Int) {
LOW(1),
MEDIUM(2),
HIGH(3),
URGENT(4);
}
fun main() {
val task = Priority.HIGH
println(task) // HIGH
println(task.level) // 3
println(Priority.values().toList()) // [LOW, MEDIUM, HIGH, URGENT]
}
Create a sealed class for payment methods and a function that processes each type:
sealed class Payment {
data class CreditCard(val number: String, val amount: Double) : Payment()
data class BankTransfer(val account: String, val amount: Double) : Payment()
data class Cash(val amount: Double) : Payment()
}
fun processPayment(payment: Payment): String = when (payment) {
is Payment.CreditCard -> "Charge $${payment.amount} to card ending ${payment.number.takeLast(4)}"
is Payment.BankTransfer -> "Transfer $${payment.amount} from ${payment.account}"
is Payment.Cash -> "Collect $${payment.amount} in cash"
}
fun main() {
println(processPayment(Payment.CreditCard("1234567890123456", 99.50)))
// Charge $99.5 to card ending 3456
}