Kotlin

Litt historikk

Java 1.0 kom ut 21. januar 1996.

 

Java er altså over 20 år gammelt, og mye er lagt til i ettertid (som ArrayList i '98, regex i '02, generics og for-each i '04).

 

Men alle valgene som ble gjort i starten må fortsatt støttes.

Det finnes ganske mange språk til JVM (Java Virtual Machine)

 

Hvorfor i det hele tatt bruke JVM til nye språk?

  • Stort økosystem
  • Lettere enn å skrive en ny runtime/VM
  • Kryssplattform (i motsetning til hva CLR var)

JVM-språk

Groovy

Skripting-språk

Språket som brukes til Gradle

Støtter 99% av Java-kode direkte

+ dynamisk typing, funksjonell programmering

println "Hello world"
// eller
System.out.println("Hello world");

def x = 1
// eller
int x = 1;

Scala

Avansert funksjonell programmering kombinert med objekt-orientering

Statisk typet

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")
  }

  def matchTest(x: Int): String = x match {
    case 1 => "one"
    case 2 => "two"
    case _ => "many"
  }
}

Clojure

Funksjonell programmering med Lisp-basert syntaks

Dynamisk typet

Kraftig makro-system

(defn -main []
  (println "Hello, World!"))

(defmacro and
  ([] true)
  ([x] x)
  ([x & rest]
    `(let [and# ~x]
       (if and# (and ~@rest) and#))))

Ceylon

Funksjonelt og objekt-orientert

Avansert statisk "flow-sensitive" typing

shared void run() => newServer {
        Endpoint {
            path = startsWith("/");
            acceptMethod = { get };
            (request, response) =>
                response.writeString("Say more, more clearly");
        }
    }.start();

Kotlin

Funksjonelt og objekt-orientert

Statisk typing (++)

Laget av JetBrains (som lager IntelliJ)

Relativt "tynt" lag over Java

fun main(args: Array<String>) {
   println("Hello World!")
}

Ny syntaks

Ikke behov for semikolon

"Bare" println

Ikke klasse rundt alt

fun main(args: Array<String>) {
   println("Hello World!")
}

Det opplagte først...

To typer variabler:

  • var : variabel - kan endres
  • val : value - kan ikke endres

 

Vi trenger vanligvis ikke spesifisere typer på variabler, men kan hvis vi vil

fun main(args: Array<String>) {
    var sum = 0
    for (s in args) {
        sum += s.toInt()
    }
    println(sum)
    
    val x = 25
    val y : Int = 10
    println(x + y)
}

Variabler

Alt er nå objekter, ikke lenger noe skille mellom int og Integer i Kotlin

(men fortsatt samme ytelse som java)

Typer fra Java

int
Integer
double
Double
int[]
Int
Int?
Double
Double?
Array<Int>

Hvis vi snakker med Java:

Når noe kan være null markerer vi det med ?

 

For å bruke verdien må vi da sjekke eller konvertere, f.eks. med ?: (elvis-operatoren)

Null safety

fun factorial(n: Int) : Int? {
    if (n < 0) return null
    var product = 1
    for (i in 1..n) {
        product *= i
    }
    return product
}

fun main(args: Array<String>) {
    val x = factorial(3) ?: 0
    println(x + 2)
}

Klasser

class Person(val name: String, var age: Int) {
  fun sayHi() {
    println("$name says: Hi!")
  }
}

fun main(args: Array<String>) {
    val erik = Person("Erik", 28)
    println(erik.name)
    erik.sayHi()
}

Konstruktør i toppen av klassen.

Klasser er final som standard

Metoder er public som standard

Ikke noe "new"

Data-klasser

data class Person(val name: String, var age: Int)

fun main(args: Array<String>) {
    val erik = Person("Erik", 28)
    println(erik) // Person(name=Erik, age=28)

    // destrukturering:
    val (name, age) = erik
}

Hvis man markerer en klasse med "data" genereres metoder som toString og equals, og man kan destrukturere klassen.

Operator overloading

data class Vec(var x: Int, var y: Int) {
    operator fun plus(b: Vec): Vec {
        return Vec(x + b.x, y + b.y)
    }
}

fun main(args: Array<String>) {
    val a = Vec(2, 3)
    val b = Vec(4, 5)
    println(a + b)
}

Kotlin konverterer bruk av operatorer som a + b til a.plus(b)

 

Men selvsagt kun dersom man definerer de riktige metodene.

== og ===

data class Vec(var x: Int, var y: Int) {
    operator fun plus(b: Vec): Vec {
        return Vec(x + b.x, y + b.y)
    }
}

fun main(args: Array<String>) {
    val a = Vec(2, 3)
    val b = Vec(3, 5)
    println(a + b == Vec(6, 8)) // true

    // I Java:
    System.out.println(a.plus(b).equals(new Vec(6, 8)));
}

Ved hjelp av operator overloading konverteres a == b til a.equals(b).

 

Hvis man faktisk vil sjekke referanselikhet (a == b for objekter i Java) bruker man a === b

When

val x = ...

when (x) {
  is TypeALegemiddel -> println(x.styrke)
  is TypeBLegemiddel -> println(x.volum)
  else -> println("Type C kanskje?")
}

When er som en moderne versjon av Javas switch/case. Du kan ikke bare sjekke tall og strenger, men bl.a. sjekke type eller verdi.

 

I tillegg sjekker Kotlin at du har dekket alle muligheter dersom du bruker when som et uttrykk.

Autocasting

fun silly(x: Int) : Any = when (x) {
    1 -> 2.0
    2 -> "Hello"
    else -> 5
}

fun main(args: Array<String>) {
    val x = silly(2)
    if (x is String) println(x.length)
}

Hvis Kotlin kan vite typen til en variabel trenger du ikke caste den for å bruke metoder o.l.

På tide å prøve selv

Du kan bruke Intellij IDEA, men uansett er det greit å sjekke ut oppgavene på:

http://try.kotlinlang.org/

 

God dokumentasjon på:

https://kotlinlang.org/docs/reference/

 

(Og begge deler er lett tilgjengelig fra forsiden)

Extensions

fun Int.square(): Int {
    return this * this
}

operator fun Int.times(b: Vec): Vec {
    return Vec(this * b.x, this * b.y)
}

Du kan utvide eksisterende klasser med nye metoder (m.m.)

Spesielt nyttig til operator overloading

Object

object Highlander {
  val id = 1
}

Deklarasjoner av object er klasser med bare en instans (en "singleton")

Sealed class

sealed class Direction {
    object North : Direction()
    object South : Direction()
    object East : Direction()
    object West : Direction()
}

fun Vec.move(dir: Direction) = when (dir) {
    Direction.North -> y += 1
    Direction.South -> y -= 1
    Direction.East -> x += 1
    Direction.West -> x -= 1
}

Når vi markerer en klasse med sealed kan den bare arves inne i klassen.

 

Det gjør det mulig for kompilatoren å se at vi har testet alle muligheter i en "when". (Her kunne man egentlig brukt enum)

Lambda

    // metode-referanse
    listOf(1, 2, 3).reduce(Int::times)

    // lambda-uttrykk må være i { }
    listOf(1, 2, 4).map({x -> x * x})

    // men lambda-uttrykk som siste parameter
    // trenger ikke stå i paranteser
    // og har man bare ett argument kan man bruke
    // "it" som navn uten å spesifisere det
    listOf(2, 4, 6).filter {it > 3}

Kotlin støtter selvsagt lambda-uttrykk og høyere ordens funksjoner.

Gradle og Maven med Kotlin:

https://github.com/JetBrains/kotlin-examples

Kotlin

By Erik Vesteraas

Kotlin

Intro til Kotlin, et moderne programmeringsspråk på JVM

  • 1,635