A Brief Tour of Kotlin

Created by Todd Ginsberg for CJUG
2016-12-13

The Gateway to St. Petersburg!

Photo courtesy of Wikimapia

So, uh, Kotlin?

  • Staticly typed JVM language
  • Targets Java 6 bytecode
  • Deploy to Android
  • Compile to JavaScript (ECMAScript 5.1)
  • Interoperates with Java
  • By JetBrains, under Apache 2 license
  • IDE plugins available for IntelliJ and Eclipse
  • 1.0 Release: February, 2016

Basics - Classes


class Customer(name: String) {
    init {
        println("Customer initialized with value ${name}")
    }
    ...
}
						
  • Classes define their constructor in the class header
  • Init blocks are executed in order and act as constructor body
  • Classes are final by default
  • Look! No semicolons!
  • Look! String interpolation!

Basics - Inheritance


open class Entity(name: String) { 
	...
}
class Customer(name: String) : Entity(name) {
    init {
        println("Customer initialized with value ${name}")
    }
    ...
}
						

Basics - var/val


var name: String = "Todd" // Initial
name = "Emma"             // OK
						

val name: String = "Todd" // Initial
name = "Emma"             // Compiler Error
						

Basics - Equality


val s1 = "CJUG"
val s2 = "cjug"
s1 == s2.toUpperCase()  // (Structural) True!
s1 === s2               // (Referential) False
						

Basics - No New Keyword


val me: Person = Person("Todd")
						

Basics - No Ternary


val status: String = if(x == 42) "Success" else "Fail"
						

Use if expression instead!

Basics - FUNctions!


fun generateRandomNumber(): Int {
    return 4
}
						

fun generateRandomNumber(): Int = 4
						

Basics - Named and Optional Parameters


fun generateRandomNumber(start: Int = 0): Int = start + 4
						

generateRandomNumber()    // 4
generateRandomNumber(1)   // 5
generateRandomNumber(start = 1)   // 5
						

Type Inference

Can't Someone Else Do It?


var result : Int = add(1, 1)
fun negate(number: Int): Int = 0-number
						

var result = add(1, 1) 
fun negate(number: Int) = 0-number 
						

Smart Casting

No need to cast


when (x) {
    is Int -> print(x % 2 == 0)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}
						

Null Safety

  • Null checking is built in
  • Must declare when you want to allow nulls

var name : String = "Todd" // Guaranteed to never be null
var salary : Int? = null   // May be null
						

Traverse safely


var homeTown : String? = null
println(homeTown.toUpperCase())  // Not allowed
						

val homeTown : String? = ...
println(homeTown?.toUpperCase()) // OK!
                ^^
						

Elvis


val lowest : Int? = listOf(1, 2, 3).min()
						

val lowest : Int = listOf(1, 2, 3).min() ?: 0
						

Combine Them!


val homeTown : String? = ...
println(homeTown?.toUpperCase() ?: "Unknown")
						

Manual Override


val lowest : Int? = listOf(1, 2, 3).min()
						

val lowest : Int = listOf(1, 2, 3).min()!!
						

val lowest : Int = listOf<Int>().min()!!
// KotlinNullPointerException!
						

Remember The Names!

?. - Safe traversal

?: - Elvis

!! - Hold my beer!!

Extension Methods

Extend existing types with your own functions!


fun String.initialCaps(): String =
    when(this.length) {
        0 ->    this
        1 ->    this.toUpperCase()
        else -> this[0].toUpperCase() + this.substring(1).toLowerCase()
    }
						

Pssst! Hey, 'when' is an expression returning a value!

Lambdas


listOf(2,3,4).map { a -> a * 2 } // [4,6,8]
						

listOf(2,3,4).map { it * 2 } // [4,6,8]
						

Lambdas are Closures


var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)
						

Can access variables in their outer scope

Properties


class Person() {
    var name: String? = null
}
						

val e = Person()
e.name = "emma"
println("Person's name is ${e.name}")
// -> "Person's name is emma"
						

Properties (continued)


class Person() {
    var name: String? = null
        set(value) {
            field = value?.initialCaps() ?: value
        }
}

val e = Person()
e.name = "emma"
println("Person's name is ${e.name}")
// -> "Person's name is Emma"
						

Propeties - Constructor


class Customer(val name: String) {
    ...
}
						

Data Classes

Java version...


public class PersonDto {
    public String firstName;
    public String lastName;
}
						
Only 4 lines

Still Java Version


public String getFirstName() {
    return firstName;
}

public void setFirstName(final String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(final String lastName) {
    this.lastName = lastName;
}
						
Up to 20 lines

Even More Java Version


@Override
public String toString() {
    return "PersonDto{" +
            "firstName='" + firstName + '\'' +
            ", lastName='" + lastName + '\'' +
            '}';
}
						
OK, 29 lines

Even Yet Still More Java Version


@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }

    PersonDto personDto = (PersonDto) o;

    if (!firstName.equals(personDto.firstName)) {
        return false;
    }
    return lastName.equals(personDto.lastName);
}
						
46 lines already?

Oh come on...


@Override
public int hashCode() {
    int result = firstName.hashCode();
    result = 31 * result + lastName.hashCode();
    return result;
}
						
A lean mean 51 lines

Kotlin Version


data class PersonDto(var firstName: String, var lastName: String)						
						
Only 1 line!

Now What?

Thanks!

 @ToddGinsberg

 #todd.ginsberg

bit.ly/KotlinTourFeedback