- In browser: https://try.kotlinlang.org
- OR in Eclipse/Intellij etc : https://github.com/Kotlin/kotlin-koans
-
- 'master' is starting point for exercises
- 'resolutions' for solution
class Greeter(val name : String) { fun greet() { println("Hello, ${name}") } } fun main(args : Array<String>) { Greeter("World").greet() }
class Greeter(private val name : String) { private var counter = 0 fun getGreeting(includeNumber: Boolean): String { return if (includeNumber) { "Hello, ${name}. Greeting message nr ${counter++}" } else { "Hello, ${name}" } } } fun main(args : Array<String>) { Greeter("World").getGreeting(true) }
Kotlin has both default and named arguments. This allows class definitions that are concise and precise
data class Person(val name: String, val country: String = "Norway", val age : Int = 18)
So, creating a new Person where you're fine with the default country, but want to change the age?
Person(name = "Arne Scheie", age = 73)
A person from Norway of age 18 could be created by
Person("Some name")
Whereas an empty constructor would give a compile error
Person() >>> error: no value passed for parameter 'name'
Java version of the same would've looked like this. equals, hashCode, getters excluded for some brevity
public Person { final String name; final String country; final Integer age; public Person(String name) { this(name, "Norway", 18); } public Person(String name, String country) { this(name, country, 18); } public Person(String name, Integer age) { this(name, "Norway", age); } public Person(String name, String country, Integer age) { this.name = name; this.country = country this.age = age; } }
val verboseLambda : (Int) -> Boolean = { i : Int -> println("lambda invoked") i % 2 == 0 } val result = verboseLambda(4) // type inference val tinyNumbers = listOf(1,2,3,4).filter{ n -> n < 3 } // single argument sugar val conciseLambda = listOf(1,2,3).filter{ it > 0}
var a: String = "abc" a = null // compilation error var b: String? = "abc" b = null
val a: String? = null if (a != null) { print("String of length ${a.length}") //good } val length: Int = a!!.length // NPE val length2: Int? = a?.length // good
data class SmallCompany(var employee: Person? = null) data class Person(var middleName: String? = null) val middleName: Int? = SmallCompany().employee?.middleName?.length // null
fun smartCast1(x: Any) { if (x is String) { print(x.length) // x is automatically cast to String } } fun smartCast2(x: Any) { if (x !is String) return print(x.length) // x is automatically cast to String }
fun ofType(x: Any) { when (x) { is String -> println(x.length) // Smart casts work for when-expressions in 1..10 -> println("x is in the range 1..10") is IntArray -> println(x.sum()) else -> println("x is unknown") } }
// Object Expression ~= anonymous inner classes in Java window.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... } })
class Car(val color = "blue") // Class object Lock { // Object declaration val i = 3 } println(Car().color) println(Lock.i)
class MyClass { companion object { fun create(): MyClass = MyClass() } } MyClass.create() // access as if "static" in class
Kotlin allows you to write custom extensions for any class. This is a power that needs to be used carefully, or you'll end up in extension hell; Kotlin's version of implicits hell
fun List<Int>.largerThanN(n: Int) = this.filter { it > n } listOf(5, 7, 8, 11, 1, 2, 3).largerThanN(5) >>> [7, 8, 11]
Kotlin also supports extension properties.
val <T> List<T>.lastIndex: Int get() = size - 1 listOf(1,2,3,4,5).lastIndex >>> 4
Kotlin's collections are immutable by default, so default constructors returns an immutable collection
val x = listOf(1) x.add(2) >>> error: unresolved reference: add val y = x + 2 >>> y: [1,2] x >>> x: [1]
If you want a mutable list you'll have to explicitly ask for it, and a mutable list works much like java's. The add method returns a boolean saying whether or not the object was added
val x = mutableListOf(1) >>> [1] x.add(2) x >>> [1,2]
They also support the usual functional collection suspects
val exampleList = listOf(1,2,3,4,5) val x = exampleList.reduce { acc, el -> acc + el } >>> x: Int x >>> 15 val (evens, odds) = listOf(1, 2, 3, 4).partition { it % 2 == 0 } evens >>> [2,4] odds >>> [1,3]
Java groupBy
Map<Gender, List<String>> groupedBy = roster.stream() .collect( Collectors.groupingBy( Person::getGender, Collectors.mapping( Person::getName, Collectors.toList())) );
Kotlin groupBy
val groupedBy: Map<Gender, List<String>> = roster.groupBy( keySelector = { it.gender }, valueTransform = { it.name } )