Kotlin
ist auch nur eine Insel!

Effiziente & sichere JVM-Entwicklung mit Kotlin

2022-06-28
Benjamin Schmid
Technology Advisor
Kotlin ist auch eine Insel: Effiziente & sichere JVM-Entwicklung mit Kotlin by Benjamin Schmid is licensed under a Creative Commons License (BY-NC-SA/4.0) .
## Was ist Kotlin?

Russische Insel

im finnischen Golf, 32km vor Petersburg

statisch typisierte
Allzweck-
Sprache
für

  • JVM
  • Javascript
  • Native (LLVM)

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

Datenblatt

Freie Software

APL 2.0 – IDE, compiler, libs, build tools

Stabil & LTS

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

JetBrains (& Google)

20+ Vollzeit & Community
„dogfooding“ & Android

Integrationen

IntelliJ, Android Studio, Eclipse,
Maven, Ant, Gradle, CLI

Historie

Februar 2016 – 1.0

Production-ready, LTS version

April 2017 – 1.1

Coroutines, Type Aliases, JS, Android

Mai 2017 – Android

Google integriert Kotlin in Android

November 2017 – 1.2

Multiplattform-Support

November 2018 – 1.3

Contracts, Unsigneds, Inline Classes

August 2020 – 1.4

SAM conversions, API mode

Mai 2021 – 1.5

JVM Records, Sealed/Inline Classes

November 2021 – 1.6

type inference & coroutines improvements

Juni 2022 – 1.7

K2 Compiler, Builder Inference

Prägnanz & Lesbarkeit

Schneller an's Ziel!
Hands on: play.kotlinlang.org

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}")
}

data 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

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

Data Classes

```kotlin data class Gast( val name: String, var zeit: Date = Date(), val title: Title? ) ```
##### Werte-Container - Um Methoden erweiterbar - unterstützt Basisklassen
###### 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!

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

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

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!

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

Java/Kotlin Interop

Mischen von Java & Kotlin-Code

bei problemlosem, gleichzeitigen Zugriff in beide Richtungen

## Kotlin & Java *Nahtlose Java ⇆ Kotlin Interoperabilität ist Top Priorität*
##### Compilert in Java 8,9,…,18 Bytecode - Versteht Kotlin & Java Code - Inkrementelle Migration gut möglich - Bidirektionale Beziehungen kein Problem
##### Kein separates Kotlin-SDK * Kotlin *nutzt direkt die JDK-Klassen* * ergänzt viele, kleine _Extensions_ – ca. 1,5MB in v1.7
### Kotlin vs. Java Types |Kotlin | → |Java | --: | -- | -- |`Any` | |`java.lang.Object` |`Int` | |`int` |`Int?` | |`java.lang.Integer` |`Array<Int>` | | `Integer[]` |`IntArray` | | `int[]`

null-Werte aus Java-Code

Alle Rückgabewerte aus Java-Code als T? zu verstehen wäre richtig,
aber extrem unkomfortabel. Kotlin setzt daher auf Platform Types T!.

Kann nicht aus Kotlin heraus erzeugt werden!

```kotlin System.getenv("_PWD").length // Potentiell NPE! ```
Volle Sicherheit kann man wiedererlangen durch: 1. Auszeichnung mit `@Nullable` in Java -> `String?` 1. Elvis Operator & Co.: `System.getenv("_PWD")?.length` 1. Auszeichnung eines `@Nullable` Defaults für ganze Pakete
Java → Kotlin: Beispiel static-Feld
Fazit: Für elegante Java-APIs teils sperriger Kotlin-Code erforderlich…
### 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` |`@JvmRecord` | Data Class als JDK14 Records

Tooling

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)

Effizienz & Idiome

#### „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)`

Extension methods & properties

Fremde Klasse um Methoden ergänzen
fun Fragment.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this.getActivity(), message, duration).show()
}
Fremde Klasse um Properties ergänzen
var EditText.stringValue: String
    get() = text.toString()
    set(str) { setText(str)  }
Verwendung
toast("Hallo Zuschauer!")
nameField.stringValue = "Max Muster"

Utilities „frei Haus“

Zahlreiche Extensions in der Kotlin Standardbibliothek (komfortabel über Code-Completion zugänglich) erweitern bestehende JDK Klassen und Interfaces
Sie helfen zu prägnanten, dennoch intuitiv lesbar Code.
val list = listOf("a", "b", "c")
for (index in list.indices.filter { it % 2 == 0 }) {
    println("$index -> ${list[index]}")
}
Anstatt in Java
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));
}
Kotlin Icon Google Android Logo

Kotlin
& Android

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

  • effiziente & fehlerarme Entwicklung
  • konzipiert für den industriellen Einsatz
  • erzeugt Java 8 Bytecode – perfekt für Android
  • 100% Java Interoperabilität – in beide Richtungen …
  • Tooling-Heimspiel – Android Studio & Gradle 3 setzen auf Kotlin
  • Aber: Overhead stdlib & Properties-Methoden

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

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

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?

Lambdas – Syntactic sugar

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

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

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

view.setOnClickListener { doSomething(it) }
##### `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

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) } } ```

Operatoren

Über Namenskonventionen. Feste Menge & Präzedenz

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!

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

Im Browser ausprobieren

play.kotlinlang.org/koans

Lernen: Kotlin Koans

Kotlin Educational Plugin mit Kotlin Koans

Quellen & Materalien

##### Materialien für den Einstieg * [*kotlinlang.org*](https://kotlinlang.org/) * [*Kotlin Koans*](https://kotlinlang.org/docs/koans.html) * [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) **Dieses Deck**
![https://bentolor.github.io/kotlin-talk/](img/qr.png)
##### 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 - [KTor](https://ktor.io/) Connected Apps - [Micronaut](https://micronaut.io/) Cloud-native - [Spring Boot](https://spring.io/guides/tutorials/spring-boot-kotlin/) Web - [SparkJava](http://sparkjava.com/) Web - [Mockk](https://github.com/mockk/mockk) Testing - [Koin](https://insert-koin.io/) DI
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/)