Restricts the class hierarchy: all subclasses must be located in the same file
sealedclassExprclassNum(valvalue: Int) : Expr()classSum(val left: Expr, val right: Expr) : Expr()funeval(e: Expr): Int=when (e) {is Num -> e.valueis Sum ->eval(e.left) +eval(e.right)}// if Expr was not sealed, when would need an else branch
Conditionals
If
If is an expression in Kotlin - it returns a value you can assign to a variable
val max =if (a > b) a else b
Ternary operator
There is no ternary operator in Kotlin
When
Similar to Java's switch statement, useful with many conditions
// Checking conditionsval (description, colour) =when { degrees <10->"cold" to BLUE degrees <25->"mild" to ORANGEelse->"hot" to RED}
// Multiple valuesfunrespondToInput(input: String) =when (input) {"y", "yes"->"I'm glad you agree""n", "no"->"Sorry to hear that"else->"I don't understand you"}
// Match on any object (using equals)funmix(c1: Color, c2: Color) =when (setOf(c1, c2)) {setOf(RED, YELLOW) -> ORANGEsetOf(YELLOW, BLUE) -> GREENsetOf(BLUE, VIOLET) -> INDIGOelse->throwException("Dirty color") }
// Checking typeswhen (pet) {is Cat -> pet.meow() // equivalent to instanceofis Dog -> pet.woof() // smart cast to the right type}when (val pet =getMyFavouritePet()) {is Cat -> pet.meow()is Dog -> pet.woof()}
Constants
Compile-time constants
The value will be inlined everywhere in the resulting bytecode. It's possible only for primitive types and String.
constval answer =42
@JvmField
Exposes a Kotlin property as a field in Java, so no getter and setter are generated.
@JvmFieldval prop =MyClass()// same as following in Javapublic static final MyClass prop = new MyClass();
Exceptions
There is no difference between checked and unchecked exceptions
val percentage =if (number in0..100) numberelsethrowIllegalArgumentException("Error!")
Try is an expression
val number =try { Integer.parseInt(string)} catch {e: NumberFormatException) {return}
Functions
Default arguments
fundisplaySeparator(character: Char='*', size: Int=10) {repeat(size) {print(character) }}displaySeparator(size =5) // equivalent to displaySeparator('*', 5)displaySeparator(character ='?') // equivalent to displaySeparator('?', 10)displaySeparator(size =3, character ='5') // valid, prints 555
Extension functions
They are defined outside of the class, but they can be called as a regular member of the class. They have to be imported in all the locations where the function is used. As they are static Java methods under the hood, no override is possible for extension functions.
funString.lastChar() =this.get(this.length -1)// fun String.lastChar() = get(length - 1) is also validval c: Char="abc".lastChar() // returns 'c'
Infix functions
infixfunInt.until(to: Int): IntRange1.until(10)1 until 10infixfun <A, B> A.to(that: B) =Pair(this, that)"ANSWER".to(42)"hot" to RED
Inline functions
The compiler will substitute the body of an inline function instead of calling it, which makes the performance overhead for inline functions to zero.
inlinefunmax(a: Int, b: Int) =if (a > b) a else b@kotlin.internal.InlineOnlypublicinlinefun <R> run(block: () -> R): R=block()// the InlineOnly annotation specifies that the function should// not be called directly but always inlined
Named arguments
println(listOf('a', 'b', 'c').joinToString( separator="", prefix="(", postfix=")")) // will print (abc)
Hello, world!
package introfunmain() {val name ="Kotlin"println("Hello, $name!") // will print Hello, Kotlin!}
Lambdas
They replace anonymous classes in Java, and allow functional programming
// Basic syntax{ x: Int, y: Int -> x+ y }// The following statements are all equivalentlist.any({ i: Int -> i>0})list.any() { i: Int -> i>0 } // valid because lambda is the last argumentlist.any { i: Int -> i>0 } // empty parentheses can be omittedlist.any { i -> i >0 } // type can be omitted if it can be inferredlist.any { it >0 } // if lambda has a single argument// Descructuring declarationsmap.mapValues { (key, value) ->"$key -> $value!" }map.mapValues { (_, value) ->"$value!" } // if one variable is not needed
{ println("hey!") }() // possible, but strangerun { println("hey!") } // usual way to run lambda
funduplicateNonZero(list: List<Int>): List<Int> {return list.flatMap {if (it ==0) returnlistOf()listOf(it, it) }}println(duplicateNonZero(listOf(3, 0, 5)) // prints [] because the return inside// the lambda makes the fun return// Correct versions list.flatMap {if (it ==0) return@flatMap listOf<Int>()listOf(it, it)}list.flatMap l@{if (it ==0) return@l listOf<Int>()listOf(it, it)}
Lambda with receiver
Similar to an extensions function, it has an implicit this set to the first argument of the with extension function
val sb =StringBuilder()with (sb) {appendln("Alphabeth: ")for (c in'a'..'z') {append(c) }toString()}
lateinit allows non-nullable properties to be initialized outside the constructor method. It cannot be a val, because that would violate immutability, and cannot be nullable or be a primitive type, as it's initialized to null under the hood. We can check if the property has been inialized by using the isinitialized property.
val list =listOf("a", "b", "c")for (s: Stringinlist) {print(s)}val map =mapOf(1 to "one", 2 to "two", 3 to "three")for ((key, value) in map) {println("$key = $value")}val list =listOf("a", "b", "c")for ((index, element) in list.withIndex()) {println("$index: $element")}
// includes the upper bound - prints 123456789for (i in1..9) {print(i)}// excludes the upper bound - prints 12345678for (i in1 until 9) {print(i)}// iterating with a step - prints 97531for (i in9 downTo 1 step 2) {print(i)}// iterating over a string - prints bcdfor (ch in"abc") {print(ch +1)}
While
while (condition) {/*...*/}do {/*...*/} while (condition)
val q ="""To code, or not to code?.."""println(q) // will print To code,// or not to code?..val r ="""To code, or not to code?..""".trimMargin()println(q) // will print To code,// or not to code?..
Nullability
The modern approach is to make a NPE a compile-time error rather than a run-time error
val s1: String="always not null"val s2: String? =nulls1.length // OKs2.length // Compiler error, s2 might be nullval length: Int? = s2?.length // equal to if (s2 != null) s2.length else nullval length: Int= s2?.length ?: 0// ?: is called the Elvis operator
val s: String?if (s ==null) returns.length // smart cast, it it passed the check above we know it's not nulls!!// if (s != null) s else null
if (any is String) { any.toUpperCase() // smart cast, any is casted to String in this block}(any as? String)?.toUpperCase() // safe cast, equivalent to// if (a is String) a else null
Object
It's the Kotlin way to express a Singleton
objectKSingleton {funfoo() {}}KSingleton.foo() // from KotlinKSingleton.INSTANCE.foo() // from Java
Companion object
Special object inside a class, useful to implement static methods in Kotlin. They can also implement a interface.
classC {companionobject {@JvmStaticfunfoo() {}funbar() {} }}// from JavaC.foo(); // works because of the @JvmStatic annotationC.Companion.foo(); // accessing the companion object from JavaC.Companion.bar(); // accessing the companion object from Java
Object expression
Replaces anonymous classes with multiple methods (if the anonymous class has a single method, it can be replaced by a lambda expresssion). This is not a singleton.
val lazyValue: Stringbylazy {println("computed!")"Hello"}
Ranges
funisLetter(c: Char) = c in'a'..'z'|| c in'A'..'Z'isLetter('q') // trueisLetter('*') // falsefunisNotDigist(c: Char) = c !in'0'..'9'isNotDigit('x') // truefunrecognize(c: Char) =when (c) {in'0'..'9'->"It's a digit!"in'a'..'z', in'A'..'Z'->"It's a letter!"else->"I don't know..."}recognize('$') // I don't know...
Sequences
Allow lazy operations on collections without creating intermediate collections. They will apply "vertical" evaluation (the first element goes through the whole chain, then the second, etc.) rather than the "horizontal" evaluation collections will perform. Nothing happens until there is a terminal operation.
listOf(1, 2, 3, 4) // [1, 2, 3, 4] .map { it * it } // [1, 4, 9, 16] .find { it >3 } // 4listOf(1, 2, 3, 4) .asSequence() // asSequence will apply map and find .map { it * it } // to 1 and 2, stopping after 2 because .find { it >3 } // a result is found
val numbers =generateSequence(3) { n -> (n +1).takeIf { n <7 }}println(numbers.first()) // does not run the lambdaprintln(numbers.toList()) // runs the lambda 4 times
val numbers =sequence {var x =0while (true) {yield(x++) }}numbers.take(5).toList() // [0, 1, 2, 3, 4]
String templates
package intro
fun main(args: Array<String>) {
val name = if (args.size > 0) args[0] else "Kotlin"
println("Hello, $name!")
}
fun main(args: Array<String>) {
println("Hello, ${args.getOrNull(0)!") // prints Hello, null! if args is empty
}
Variables
val variable1 // read-only, corresponds to final value in Java
var variable2 // mutable