init Blocks and Validation
Part of the Object Oriented Programming section of Coddy's Kotlin journey. Lesson 4 of 57.
An init block runs when an instance is created, right after the constructor parameters are assigned. It is the place for setup code that does not fit in a single property declaration:
class Order(val id: Int, items: List<Int>) {
val total: Int
val count: Int
init {
total = items.sum()
count = items.size
println("created order $id")
}
}Inside main:
val o = Order(7, listOf(4, 6, 10))
println("${o.count} items, total ${o.total}")Output:
created order 7
3 items, total 20require(condition) { message } checks an argument and throws IllegalArgumentException with the message when it is false. Putting it in init means an invalid instance can never be created:
class Percentage(val value: Int) {
init {
require(value in 0..100) { "percentage must be 0-100, was $value" }
}
}Inside main:
println(Percentage(42).value)
try {
Percentage(150)
} catch (e: IllegalArgumentException) {
println(e.message)
}Output:
42
percentage must be 0-100, was 150A class can have several init blocks; they run in order, together with the property initializers, from top to bottom:
class Demo(name: String) {
val first = "first: $name".also { println(it) }
init { println("init 1") }
val second = "second".also { println(it) }
init { println("init 2") }
}Inside main:
Demo("x")
println("done")Output:
first: x
init 1
second
init 2
doneValidation in init keeps the rule next to the data. Code that receives an instance can trust it without checking again:
class Email(val address: String) {
init {
require("@" in address) { "not an email: $address" }
}
}
fun send(to: Email) { /* no need to check the address again */ }Challenge
EasyComplete the class Password. Its init block checks, with require, that the text has at least 8 characters (message too short) and contains a digit (message needs a digit), in that order. The property strength is computed in init: strong when the text also contains an upper-case letter, otherwise ok.
The supplied code creates a Password for each input line and prints strength or the error message.
Your code goes in Password.kt. Main.kt holds the supplied input/output code and cannot be edited.
Try it yourself
fun main() {
// Supplied input/output code: keep it as it is
val input = generateSequence(::readLine).toList()
for (line in input) {
try {
println(Password(line).strength)
} catch (e: IllegalArgumentException) {
println(e.message)
}
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesPrimary Constructorsinit Blocks and ValidationSecondary ConstructorsRecap - Library Card4Inheritance
Open Classes and SubclassesOverriding Memberssuper and ConstructorsAbstract ClassesRecap - Staff Directory2Properties and Encapsulation
Custom AccessorsVisibility ModifiersPrivate Setterslazy and lateinitRecap - Bank Account5Interfaces
Declaring InterfacesDefault MethodsMultiple InterfacesInterfaces as TypesRecap - Payment Methods8Generics
Generic FunctionsGeneric ClassesType ConstraintsVariance With in and outRecap - Priority Queue11Project: Library Management
Books and MembersBorrowing BooksPractice on your own: Kotlin playground