Classes Workbook (Part 1)
Eleven short exercises on Kotlin classes — declarations, primary constructors, properties, init blocks, custom accessors, encapsulation, and inheritance.
Practice problems for A Kotlin Class Is Mostly Its Header. Each takes a minute or two. A few exercises auto-check: write the class so its method returns the right value, press Run, and hidden tests go green when you’re right and red (with a hint) when you’re not, all on JetBrains’ Kotlin server. Most are declaration exercises that stay attempt-then-reveal: fill in the TODO, press Run to compile it, then click Show answer to check your work. Nothing here is a trick question, just direct practice of the syntax from the lesson.
declarations and constructors
1. The smallest class
Declare an empty class Person and create an instance.
fun main() {
// TODO: declare a class Person, then create an instance and print it
val p = Any()
println(p)
} Show answer Hide answer
class Person
val p = Person()No new keyword — you call the class like a function.
2. Properties in the header
Declare Point with a read-only x and y of type Int in its primary constructor.
// TODO: give Point a read-only x and y of type Int in its primary constructor
class Point
fun main() {
val p = Point() // then construct Point(3, 4) and print p.x and p.y
println(p)
} Show answer Hide answer
class Point(val x: Int, val y: Int) 3. A default property value
Declare User(name: String) with a var active property that defaults to true.
// TODO: add a var 'active' property that defaults to true
class User(val name: String)
fun main() {
val u = User("Ada")
println(u.name) // then also print u.active
} Show answer Hide answer
class User(val name: String, var active: Boolean = true) 4. An init block
Declare Account(balance: Int) that throws IllegalArgumentException at construction if balance is negative.
// TODO: throw in an init block when balance is negative (use require)
class Account(val balance: Int)
fun main() {
val a = Account(100)
println(a.balance) // then try Account(-5)
} Show answer Hide answer
class Account(val balance: Int) {
init {
require(balance >= 0) { "balance must be non-negative" }
}
} 5. Construct with named arguments
Given class Server(val host: String, val port: Int = 80, val secure: Boolean = false), construct one for "example.com" that is secure, keeping the default port.
class Server(val host: String, val port: Int = 80, val secure: Boolean = false)
fun main() {
// TODO: construct a secure Server for "example.com", keeping the default port
val s = Server("example.com")
println(s.host + " " + s.port + " " + s.secure)
} Show answer Hide answer
Server(host = "example.com", secure = true) 6. A secondary constructor
Add a secondary constructor to Point that takes a single Int and uses it for both x and y.
// TODO: add a secondary constructor taking one Int, used for both x and y
class Point(val x: Int, val y: Int)
fun main() {
val p = Point(3, 4)
println(p.x.toString() + ", " + p.y) // then try Point(5)
} Show answer Hide answer
class Point(val x: Int, val y: Int) {
constructor(both: Int) : this(both, both)
} properties and encapsulation
7. A computed property
Add a read-only area property to Rectangle(val w: Int, val h: Int), computed from its sides, so Rectangle(3, 4).area is 12.
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun computesArea() {
Assert.assertEquals("area is w * h",
12, Rectangle(3, 4).area)
Assert.assertEquals("area of a unit square",
1, Rectangle(1, 1).area)
}
}
//sampleStart
class Rectangle(val w: Int, val h: Int) {
val area: Int
get() = 0 // TODO: compute the area from w and h
}
//sampleEnd Show answer Hide answer
class Rectangle(val w: Int, val h: Int) {
val area: Int
get() = w * h
} 8. Read-public, write-private
Declare Counter with an Int property value that anyone can read but only the class can change, plus an increment() method.
// TODO: make 'value' read-public but write-private, and add increment()
class Counter {
var value: Int = 0
}
fun main() {
val c = Counter()
println(c.value) // then call increment() a few times and print again
} Show answer Hide answer
class Counter {
var value: Int = 0
private set
fun increment() { value++ }
} inheritance
9. Classes are final by default
Animal has an open speak() returning "...". Override speak() in Dog so it returns "woof".
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun speaks() {
Assert.assertEquals("Animal speaks the default",
"...", Animal().speak())
Assert.assertEquals("Dog overrides to woof",
"woof", Dog().speak())
}
}
//sampleStart
open class Animal {
open fun speak() = "..."
}
// TODO: override speak() so a Dog returns "woof"
class Dog : Animal()
//sampleEnd Show answer Hide answer
open class Animal {
open fun speak() = "..."
}
class Dog : Animal() {
override fun speak() = "woof"
}A class and its members must be marked open before they can be extended or overridden.
10. An abstract class
Shape is abstract with an abstract area(): Double. Implement area() in Circle as Math.PI * r * r.
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun circleArea() {
Assert.assertEquals("area is pi times r squared",
Math.PI * 2.0 * 2.0, Circle(2.0).area(), 0.0001)
}
}
//sampleStart
abstract class Shape {
abstract fun area(): Double
}
class Circle(val r: Double) : Shape() {
override fun area(): Double =
0.0 // TODO: Math.PI * r * r
}
//sampleEnd Show answer Hide answer
abstract class Shape {
abstract fun area(): Double
}
class Circle(val r: Double) : Shape() {
override fun area() = Math.PI * r * r
} 11. Make it extendable
This doesn’t compile. Fix the declaration so Derived can extend Base:
class Base
class Derived : Base()
// TODO: make Base extendable so Derived can subclass it
class Base
class Derived // then: class Derived : Base()
fun main() {
val d = Derived()
println(d)
} Show answer Hide answer
open class Base
class Derived : Base()A class is final by default; open is what lets it be extended.
Going deeper: backing fields and factories
12. A validating setter
Give celsius a custom setter that rejects anything below absolute zero (-273.15) and otherwise stores the value. Use the backing field.
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun setter() {
val t = Temperature(20.0)
t.celsius = 25.0
Assert.assertEquals(25.0, t.celsius, 0.001)
try { t.celsius = -300.0; Assert.fail("should reject") }
catch (e: IllegalArgumentException) { /* expected */ }
}
}
//sampleStart
class Temperature(c: Double) {
var celsius: Double = c
// TODO: reject values below -273.15, otherwise store via the backing field
}
//sampleEnd Show answer Hide answer
class Temperature(c: Double) {
var celsius: Double = c
set(value) {
require(value >= -273.15) { "below absolute zero" }
field = value
}
}Assign field, not celsius — writing celsius = value would call the setter again, forever.
13. Force creation through a factory
Make Email’s constructor private and add a companion factory of that returns an Email?, non-null only when the input contains @.
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun factory() {
Assert.assertEquals("[email protected]", Email.of("[email protected]")?.address)
Assert.assertNull(Email.of("nonsense"))
}
}
//sampleStart
class Email(val address: String) {
// TODO: make the constructor private; add companion fun of(raw): Email?
}
//sampleEnd Show answer Hide answer
class Email private constructor(val address: String) {
companion object {
fun of(raw: String): Email? = if ("@" in raw) Email(raw) else null
}
}A private constructor plus a companion factory is Kotlin’s validated-construction pattern.
14. Construction order
Property initializers and init blocks run in source order, interleaved. Given a property a, then an init, then property b, then another init, what order do they run in? Reveal to check.
Show answer Hide answer
Top to bottom as written: a’s initializer, then the first init, then b’s initializer, then the second init. That’s why an init block can only reference properties declared above it — anything below is still uninitialized.
Back to the lesson, A Kotlin Class Is Mostly Its Header, or on to the next one: class types.
Comments