eXXcellent solutions GmbH Logo Herbstcampus Logo

Workshop : Effiziente & sichere JVM-Entwicklung mit Kotlin

2019-09-03
Benjamin Schmid
Technology Advisor
Java++: Effiziente & sichere JVM-Entwicklung mit Kotlin by Benjamin Schmid is licensed under a Creative Commons License (BY-NC-SA/4.0) . Creative Commons Lizenzvertrag
## Was ist Kotlin?

Russische Insel

im finnischen Golf, 32km vor Petersburg

statisch typisierte
Allzweck-
Sprache
für

  • JVM
  • Browser
  • Native (LLVM)

## Prägnanz ## Lesbarkeit ## Sicherheit ## Interoperabilität ## Tooling

Datenblatt

Freie Software

APL 2.0 – IDE, compiler, libs, build tools

backed by JetBrains

20 Vollzeit & Community
„dogfooding“: 0,5 Mio. LOC

Stabil

Start: ~2010, v1.0 Feb. '16
Long-term backward compatibility

Integrationen

IntelliJ, Eclipse, Netbeans
Maven, Ant, Gradle, CLI

Historie

Juli 2011

Offizielle Vorstellung

1.0 – Februar 2016

Production-ready, LTS version

1.1 – April 2017

Coroutines, Type Aliases, JS, Android

Mai 2017

Google integriert Kotlin in Android

1.2 – November 2017

Multiplattform-Support

1.3 – November 2018

Contracts, Unsigneds,
Inline Classes

Prägnanz vs. Lesbarkeit

import java.util.*

fun main(args: Array<String>) {
    val name = if (args.size > 0) args[0] else "Publikum"
    val zuschauer = Gast(name, title = Title.wertes)

    println(zuschauer)
    println("Hallo ${zuschauer.title} ${zuschauer.name}")
}

class Gast(val name: String,
           var zeit: Date = Date(),
           val title: Title)
enum class Title { Herr, Frau, wertes }                

← Optional semicolons

← Top-level functions

← Kein new,
     named Params

← String interpolation



← Default values
valpublic final

Kotlin Entwurfsprinzipien

in a nutshell

## Kotlin Targets „Statically typed, run anywhere!“ * **JVM** * **Android** * **Browser** (Javascript) * **Native**, z.B. iOS (via LLVM)

Kotlin Icon + Google Android Logo – Ein Dream-Team?

  • effiziente & fehlerarme Entwicklung
  • konzipiert für den industriellen Einsatz
  • erzeugt Java 6 bzw. 8 Bytecode – perfekt für Legacy & Android
  • 100% bidirektionale Java Interoperabilität
  • Tooling-Heimspiel
  • Geringe Runtimegröße & teils sogar weniger Overhead
### Die Java Virtual Machine (JVM) Java = *JVM* (Bytecode, Memory Model, GC, Hotspot) \+ *JRE* (Standard-Bibliothek wie `java.lang.*`) \+ *JDK* (Compiler & Tools)
## Kotlin & Java *Nahtlose Java ⇆ Kotlin Interoperabilität ist Top Priorität* * Compiliert in Java 6 bzw. 8 Bytecode * damit legacy- & Android-friendly * Compiler versteht Kotlin & Java-Code * Kein separates Kotlin-SDK * Kotlin *nutzt direkt die JDK-Klassen* * ergänzt viele, kleine _Extensions_ – ca. 736KB in 1.0.6

Java/Kotlin Interop

Mischen von Java & Kotlin-Code

bei problemlosem, gleichzeitigen Zugriff in beide Richtungen

### Schrittweise Einführung von Kotlin im Projekt damit problemlos möglich!

Tooling: Design Goals

Kotlin refactoring options

Automatische Konvertierung
Java → Kotlin

![Convert Java Classes](img/convert-java.png) ![Convert Java Code from Clipboard](img/kotlin-copypaste-convert.png)

Quick-Start

Basics

Session #1: Basics

Type inference

```kotlin fun noTypeInfer(vals: List<Int>): Unit { val distinct: Set<Int> = vals.toSet() val first: Int = vals[0] val negs: List<Int> = vals.filter( fun(n: Int): Boolean { return n < 0 } ) } ```
```kotlin fun typeInfer(vals: List<Int>){ val distinct = vals.toSet() val first = vals[0] val negs = vals.filter( fun(n) = n < 0 ) } ```

Typangaben üblicherweise nur an Schnittstellen erforderlich

Klassen

Properties

  • Klassen in Kotlin kennen keine Felder, nur Properties
  • Compiler generiert Getter- & Setter sowie Backing Field
  • Properties of Android Intent class getX()/setX()-Paare
    aus Java erscheinen
    als Property x
  • Properties unterstützen Delegation

Session #2: Klassen

Data Classes

```kotlin data class Gast( val name: String, var zeit: Date = Date(), val title: Title? ) ```

Werte-Container

- Um Methoden erweiterbar - unterstützt Basisklassen seit Kotlin 1.1
###### Kotlin übernimmt
- Getter & Setter - `equals()`
- `hashCode()` - `toString()`
- `copy()` für Varianten - `componentN()` für Destructuring

... exakt dasselbe mit Java

public final class Gast {
  @NotNull
  private final String name;
  @NotNull
  private Date zeit;
  @Nullable
  private final Title title;

  @NotNull
  public final String getName() {
    return this.name;
  }

  @NotNull
  public final Date getZeit() {
    return this.zeit;
  }

  public final void setZeit(@NotNull Date date) {
    checkParameterIsNotNull(date, "<set-?>");
    this.zeit = date;
  }

  @Nullable
  public final Title getTitle() {
    return this.title;
  }

  public Gast(@NotNull String name,
              @NotNull Date zeit,
              @Nullable Title title) {
    checkParameterIsNotNull(name, "name");
    checkParameterIsNotNull(zeit, "zeit");
    this.name = name;
    this.zeit = zeit;
    this.title = title;
  }
  public Gast(String string, Date date,
              Title title, int n) {
    if ((n & 2) != 0) {
      date = new Date();
    }
    this(string, date, title);
  }

  @NotNull
  public final String component1() {
    return this.name;
  }

  @NotNull
  public final Date component2() {
    return this.zeit;
  }

  @Nullable
  public final Title component3() {
    return this.title;
  }

  @NotNull
  public final Gast copy(@NotNull String name,
                         @NotNull Date zeit,
                         @Nullable Title title) {
    checkParameterIsNotNull(name, "name");
    checkParameterIsNotNull(zeit, "zeit");
    return new Gast(name, zeit, title);
  }

  public String toString() {
    return "Gast(name=" + this.name +
           ", zeit=" + this.zeit +
           ", title=" + this.title + ")";
  }
  public int hashCode() {
    String s = this.name;
    Date d = this.zeit;
    Title t = this.title;
    return ((s != null ? s.hashCode() : 0) * 31
          + (d != null ? d.hashCode() : 0)) * 31
          + (t != null ? t.hashCode() : 0);
  }

  public boolean equals(Object object) {
    if (this == object) return true;

    if (!(object instanceof Gast))
        return false;

    Gast gast = (Gast)object;
    if (!areEqual(this.name, gast.name)
        || !areEqual(this.zeit, gast.zeit)
        || !areEqual(this.title, gast.title))
        return false;

    return true;
  }
}

… 3 Attribute!

Prägnanz & Lesbarkeit

Schneller an's Ziel!
#### „Wie ist denn das Wetter?“ in Java ``` public class WeatherIndicator { void rateWeather(int celsius) { String status; Color color; if (celsius < 5) { status = "Saukalt!"; color = Color.BLUE; } else if (celsius >= 5 && celsius <= 20) { status = "Geht so!"; color = Color.ORANGE; } else { status = "Urlaub!"; color = Color.RED; } } } ```
#### ... in Kotlin ``` class WeatherIndicator { fun rateWeather(celsius: Int) { val status: String val color: Color if (celsius < 5) { status = "Saukalt!" color = Color.BLUE } else if (celsius >= 5 && celsius <= 20) { status = "Geht so!" color = Color.ORANGE } else { status = "Urlaub!" color = Color.RED } } } ```
#### ...mit Range Check ``` class WeatherIndicator { fun rateWeather(celsius: Int) { val status: String val color: Color if (celsius < 5) { status = "Saukalt!" color = Color.BLUE } else if (celsius in 5..20) { status = "Geht so!" color = Color.ORANGE } else { status = "Urlaub!" color = Color.RED } } } ```
#### ... mit Destructuring & `if`-Expression ``` class WeatherIndicator { fun rateWeather(celsius: Int) { val (status: String, color: Color) = if (celsius < 5) { Pair("Saukalt!", Color.BLUE) } else if (celsius in 5..20) { Pair("Geht so!", Color.ORANGE) } else { Pair("Urlaub!", Color.RED) } } } ```
#### ... mit Type Inference durch `Pair<A,B>` ``` class WeatherIndicator { fun rateWeather(celsius: Int) { val (status, color) = if (celsius < 5) { Pair("Saukalt!", Color.BLUE) } else if (celsius in 5..20) { Pair("Geht so!", Color.ORANGE) } else { Pair("Urlaub!", Color.RED) } } } ```
#### ... mit Pattern Matching über `when` ``` class WeatherIndicator { fun rateWeather(celsius: Int) { val (status, color) = when { celsius < 5 -> Pair("Saukalt!", Color.BLUE) celsius in 5..20 -> Pair("Geht so!", Color.ORANGE) else -> Pair("Urlaub!", Color.RED) } } } ```
#### ... mit Standbard-Bibliotheks Extension ```kotlin class WeatherIndicatorKt { fun rateWeather(celsius: Int) { val (status, color) = when { celsius < 5 -> "Saukalt!" to Color.BLUE celsius in 5..20 -> "Geht so!" to Color.ORANGE else -> "Urlaub!" to Color.RED } } } ``` `fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)`

Extensions

Extension methods

fun String.lastChar() = this.get(this.length - 1)
  • … erlauben das Erweitern beliebiger Klassen
  • this zeigt auf das erweitertete Objekt
  • insbesondere im Android-Umfeld sehr hilfreich (z.B. Anko)
// Verwendung
val c = "Hallo Zuschauer!".lastChar()

Extension properties

// Extension property für das Android EditText Widget
var EditText.stringValue: String
    get() = text.toString()
    set(str) { setText(str)  }
// Verwendung
nameField.stringValue = "Max Muster"

Lambdas

Funktionen als
first-class elements

Funktionen sind Sprachelemente erster Klasse und können daher auch als Variablen, Properties, Parameter und Rück­gabe­werte genutzt werden.

Durch Verschachtelung lassen sich Funktionen höherer Ordnung (Higher-order functions) erschaffen

Lambdas – Syntactic sugar

view.setOnClickListener({ e -> doSomething(e) })

view.setOnClickListener() { e -> doSomething(e) }

view.setOnClickListener { e -> doSomething(e) }

view.setOnClickListener { doSomething(it) }

Utilities „frei Haus“

Zahlreiche Extensions in der Standardbibliothek bereits enthalten.
Sie erweitern bestehende Klassen und Interfaces und sind
komfortabel über Code-Completion auffindbar.
List<String> list = Arrays.asList("a","b","c");
for (int index = 0; index < list.size(); index++) {
    if (index % 2 == 0)
        System.out.println(index + " -> " + list.get(index));
}
Sie helfen wesentlich beim prägnanten, dennoch intuitiv lesbar Code.
val list = listOf("a", "b", "c")
for (index in list.indices.filter { it % 2 == 0 }) {
    println("$index -> ${list.get(index)}")
}
Im Beispiel: listOf, indices, filter. Auch Bibliotheken für Android (Anko)

Sicherheit

First-class immutables

Nur-lesbare Variablen aufwandsfrei durch val statt var

Standard-Collection Interfaces (List, Set, …) sind read-only

Zum ändern muss man z.B. MutableList statt List nutzen

Klassen & Methoden sind final by Default

open class ExtendableClass {
    open fun overridableMethod() { … }
}

class Derived: ExtendableClass() {
    override fun overridableMethod() { … }
}
Mutige Entscheidung!
… Framework-Entwickler müssen Acht geben!

Session #3: Immutables

The
billion-dollar
mistake

Tony Hoare
null und seine Folgen

Nullwert-Sicherheit

Nur explizit Nullwert-fähige Typen akzeptieren null.
Ungeprüfte Zugriffe führen zu Compile-Fehlern!

```kotlin var nullable: String? = null var nonNullable: String = "Hi!" // Ab hier compiliert nichts mehr … nonNullable = nullable nonNullable = null nullable.isEmpty() ```

Nullwerte sind
Bestandteil des Typsystems!

An den Schnittstellen baut Kotlin Prüfungen & @NotNull Annotationen ein.

Effizienter Umgang mit Nullwert-Typen

Effizienter Umgang mit Nullwert-Typen

Interoperabilität

### Kotlin vs. Java Types |Kotlin | → |Java | --: | -- | -- |`Any` | |`java.lang.Object` |`Int` | |`int` |`Int?` | |`java.lang.Integer` |`Double`| |`double` |`Double?`| |`java.lang.Double` |`Array<Int>` | | `Integer[]` |`IntArray` | | `int[]`

null-Werte aus Java-Code

Alle Rückgabewerte aus Java-Code als T? zu verstehen
wäre zwar richtig, aber extrem unkomfortabel.

Daher besitzt Kotlin das Konzept sog. Platform Types T!.

```kotlin System.getenv("_PWD").length // Potentiell NPE! ```
Kann nicht aus Kotlin heraus erzeugt werden!
### Java Interoperabilität – Allgemeines ##### Kotlin → Java Klassenweises _Mischen von Java & Kotlin-Code_ problemlos - Jeglicher Java-Code kann elegant & direkt genutzt werden - Inkrementelle Migration gut möglich - Java 8 seit Kotlin 1.1, Java 9 Module seit Kotlin 1.2 ##### Java → Kotlin Elegante APIs erfordern gelegentlich sperrige Kotlin-Idiome.

Beispiel: static-Feld

kein direktes static in Kotlin, sondern companion object

### Kotlin Interop-Features |Element | Beschreibung | -- | -- | |`@JvmStatic` | Statische Methode / Property |`companion object` | Singleton / static Object |`@JvmField` | Property als Java Feld verfügbar machen |`@JvmName` | Bytecode/JVM Namen steuern |`@JvmOverloads` | Optionale Parameter ausmultiplizieren |`@Throws` | Checked exceptions Signatur deklarieren |`@Nullable / @NotNull` | `T?` vs. `T`

Effizienz & Idiome

Ausdrücke & Konventionen

Mehr Ausdrücke (if, ?:, ?., …)
Pattern matching (when)
String Templates
Syntactic Sugar

listOf(…), repeat(3){…},
with(x){…}, x.apply{…}, …

public sind Default
kein new; optional
Operatoren & Dekomposition über Namen

Ausdrucksstärke

Standard-Bibliothek

Konventionen

Kotlin Icon Google Android Logo

Kotlin
& Android

Im Mai 2017 – die Antwort

https://heise.de/-3717940

Google Android Logo on speed

Typischer Android-Boilerplate ...

EditText editTitle = (EditText) v.findViewById(R.id.edit_title);
editTitle.setText(mItem.getTitle());

CheckBox enabledBox = (CheckBox) v.findViewById(R.id.enable_box);
enabledBox.setChecked(true);

Button createButton = (Button) v.findViewById(R.id.create_entry);
createButton.setOnClickListener(new OnClickListener() {
    @Override public void onClick(View button) {
        createElement();
    }
});

… ständige Lookups & Typecasts von Views über findViewById().

Kotlin Extensions aktivieren

Add Kotlin Extensions to Gradle Build

Virtuelle Properties
für UI-Elemente

Das Kotlin Android Extensions erzeugt virtuelle mylayout.xml-Pakete

import kotlinx.android.synthetic.main.mylayout.*
edit_title.setText(mItem.title)
enable_box.isChecked = true
create_entry.setOnClickListener { createElement() }

Ein einfacher Import erlaubt typsicheren Zugriff auf alle
darin enthaltene View-Elemente direkt als simples Property.
Ohne zusätzlichen Code, Annotations oder Runtime!

Hello Android: Layout

Hello Android: Code part #1

Hello Android: Code part #1

Hello Android: Code part #2

Hello Android: Code part #1

Kotlin Icon – Advanced

Higher-order functions – Beispiel

class FirstClassFunction(
        val f1: (String) -> Int,
        val f2: (Int) -> Boolean) {

    fun strToBool(str: String): Boolean {
        val f: (String) -> Boolean = higherOrderFun()
        return f(str)
    }

    private fun higherOrderFun(): (String) -> Boolean {
        return { x -> f2(f1(x)) }
    }
}

fun main(args: Array<String>) {
    val c = FirstClassFunction(
                Integer::parseInt,
                { it % 2 == 0 }
            )
    val strings = listOf("2", "7", "8")
    println(strings.map(c::strToBool))
}

 
← Funktion als Parameter
 
 
 
← Funktion als Variable
 
 
 
← Funk. als Rückgabewert
Kombination via Lambda


 
 
 
← Funk.-Referenz
Lambda-Ausdruck
 
 
Was kommt raus?

##### `with(…) { }`: Parameter wird im Block zum `this` ``` private fun toJSON(aha: Aha): JSONObject { with(JSONObject()) { put(JSON_ID, aha.id.toString()) put(JSON_TITLE, aha.title) put(JSON_USEFUL, aha.isUseful) return this } } ``` ##### *Varianten* `apply()`, `let()` und `use()` als try-with–Equivalent

GUI & null – Typische Konstellation

Werte für Initialisierung erst im Android-Callbacks bekannt … ```kotlin class EditPersonActivity : FragmentActivity() { var person: Person var anrede: String var imm: InputMethodManager … override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val personId = savedInstanceState?.getInt("value",0) person = PersonManager.load(id) anrede = resources.getString(R.string.anrede) imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager } } ```
So nicht möglich!

Lösungen: Verzögerte Initalisierung

##### `lateinit` Properties (empfohlen) ```kotlin lateinit var person: Person ```
##### Delegation des Properties ```kotlin var anrede: String by Delegates.notNull() ```
##### Lazy-Initalisierung ```kotlin val imm: InputMethodManager by lazy { getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager } ```
Wirft sprechende Exception, falls ohne Initialisierung gelesen wird!

Operatoren

Über Namenskonventionen. Feste Menge & Präzedenz

Typsichere DSLs über „Empfänger“-Objekt

Es ist möglich, Funktionssignaturen zu definieren, die Lambda-Ausdrücke erwarten bei denen this auf Zielobjekte eines bestimmten Typs zeigen.

Darüber sind typsichere DSLs möglich:

```kotlin class HTML { fun body() { ... } } fun html(init: HTML.() -> Unit): HTML { val html = HTML() html.init() return html } html { body() } ```

 
 
 
 
← Erwartet Funktion mit this vom Typ HTML
← Erstellung des receiver object
← Führe Lambda auf receiver object aus



← Kurzform von html({…})
Lambda-Ausdruck für einen HTML-„Empfänger“

Gradle Kotlin DSL – build.gradle.kts

```kotlin import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { base kotlin("jvm") version "1.2.0" apply false } allprojects { group = "org.gradle.kotlin.dsl.samples.multiprojectci" version = "1.0" repositories { jcenter() } } // Configure all KotlinCompile tasks on each sub-project subprojects { tasks.withType<KotlinCompile> { println("Configuring $name in project ${project.name}...") kotlinOptions { suppressWarnings = true } } } dependencies { // Make the root project archives configuration depend on every subproject subprojects.forEach { archives(it) } } ```

Inlining

Inlining

Infix Notation

```kotlin infix fun Int.shl(x: Int): Int { … } 1 shl 2 // als Equivalent zu 1.shl(2) ```
1. Sind Member-Funktionen oder Extension Methods 2. Haben einen einzigen Parameter 3. Und sind mit dem `infix` Schlüsselwort markiert

Fazit

&
Ausblick

Im Browser ausprobieren

Kotlin Koans

http://try.kotl.in/

Quellen & Materalien

##### Materialien für den Einstieg * [*kotlinlang.org*](https://kotlinlang.org/) * [*Kotlin Koans*](https://github.com/Kotlin/kotlin-koans) auch online: [try.kotl.in](http://try.kotl.in) in IntelliJ: [Kotlin Edu Plugin](https://blog.jetbrains.com/kotlin/2016/03/kotlin-educational-plugin/) * [Ein Überblick über Java-Alternativen für den industriellen Einsatz](http://www.heise.de/developer/artikel/Ein-Ueberblick-ueber-Java-Alternativen-fuer-den-industriellen-Einsatz-2074554.html), Benjamin Schmid
##### Referenzen - [Sprachreferenz](http://kotlinlang.org/docs/reference/) ##### Verzeichnisse - [*Awesome Kotlin* `http://kotlin.link`](http://kotlin.link/) - [OSS Projects & Libraries](https://kotlinlang.org/docs/resources.html) ##### Diverses - [@kotlin](https://twitter.com/kotlin), [@bentolor](https://twitter.com/bentolor), [github.com/bentolor](https://github.com/bentolor)
##### Nennenswerte Frameworks - [KAndroid](https://github.com/pawegio/KAndroid) - [Fuel](https://github.com/kittinunf/Fuel) (HTTP f. Android) - [RxKotlin](https://github.com/ReactiveX/RxKotlin) (Reactive) - [spek](https://github.com/jetbrains/spek) (Testing) - [kovenant](https://github.com/mplatvoet/kovenant) (Android Promises) ##### Kotlin-optimierte Addons - [SparkJava](http://sparkjava.com/) - [jooby](http://jooby.org/)
Bildnachweis: [Kotlin Islands Maps](https://commons.wikimedia.org/wiki/File:Kotlin_Island_1706.png), [Atomic bomb explosion](https://www.pond5.com/stock-footage/37592494/fireball-and-smoke-formation-out-atomic-blast.html), [Android Robot](https://commons.wikimedia.org/wiki/File:Android_robot.svg), [Kotlin Logo](https://resources.jetbrains.com/assets/products/kotlin/kotlin_logos.zip), [Hand-written arrows](http://designercandies.net/hand-drawn-arrows-brush/)