You are currently viewing Kotlin 1.6.0 veröffentlicht

Kotlin 1.6.0 veröffentlicht

Kotlin 1.6.0 ist jetzt offiziell veröffentlicht – mit Stable-Status für abschließende when-Blöcke, dem Code-Coverage-Tool Kover und einem neuen Speichermanager für Kotlin/Native. Auch andere Neuerungen in der Sprache und den Standardbibliotheken, die in 1.5.30 veröffentlicht wurden, wurden in den Status Stable befördert. Vielen Dank für das Feedback, das Sie uns zukommen ließen. Wenn Sie diese neuen Features noch nicht ausprobiert haben – jetzt ist es höchste Zeit!

In diesem Blogbeitrag finden Sie einen Überblick über die folgenden Updates:

  • Neue Sprachmerkmale: versiegelte when-Anweisungen, suspendierende Funktionen und entsprechende Umwandlungen, Instanziierung von Annotationsklassen, verbesserte Typinferenz für reguläre und Builder-Typen.
  • Kotlin/JVM mit optimierten delegierten Eigenschaften und wiederholbaren Annotationen.
  • Kotlin/Native mit der Preview eines neuen Speichermodells, Xcode-13-Unterstützung, Kreuzkompilierung von Windows-Zielen, LLVM- und Linker-Updates, Leistungsupdates und einer einheitlichen Compiler-Plugin-ABI.
  • Optionale Deaktivierung des Herunterladens von Node.js und Yarn für Kotlin/JS.
  • Ankündigung von Kover.
  • Standardbibliothek mit neuen Funktionen für die Standardeingabe, stabiler typeOf()-Funktion, stabiler Duration-API und weiteren stabilen stdlib-Funktionen.

Abonnieren Sie unser Blog, um keine Kotlin-Updates zu verpassen!

So führen Sie das Update durch

Wenn Sie IntelliJ IDEA oder Android Studio verwenden, haben Sie die Möglichkeit, automatisch auf die neue Kotlin-Version zu aktualisieren.

Weitere Informationen zur Installation von Kotlin 1.6.0.

Auf Kotlin 1.6.0 aktualisieren

Sprachmerkmale

Wir haben Sprachmerkmale aus Kotlin 1.5.30 basierend auf Ihrem Feedback stabilisiert.

Abschließende (versiegelte) when-Anweisungen

Versiegeltes when ist eine lang erwartete Funktion des Kotlin-Compilers, die Sie warnt, wenn Ihre when-Anweisungen nicht abschließend sind, also nicht alle Möglichkeiten berücksichtigen. Dadurch wird Ihr Code sicherer, ohne eigene Funktionen für diesen Zweck schreiben zu müssen. 

Kotlin hat when-Ausdrücke, die sich auf versiegelte Klassen, Enums und boolesche Typen bezogen, schon immer auf Vollständigkeit überprüft. Dies ist nützlich, wenn Sie Ihre Domäne mit diesen algebraischen Datentypen abbilden. Sie könnten beispielsweise in Ihrer App unterschiedliche Kontaktpräferenzen der Benutzer*innen als versiegelte Klassenhierarchie modellieren:

sealed class Contact {
   data class PhoneCall(val number: String) : Contact()
   data class TextMessage(val number: String) : Contact()
   data class InstantMessage(val type: IMType, val user: String) : Contact()
}

Wenn Sie nun einen Ausdruck schreiben, der abhängig von der Kontakteinstellung ein unterschiedliches Ergebnis zurückgibt, meldet der Compiler einen Fehler, wenn Sie vergessen haben, alle Typen zu berücksichtigen, die in Ihrer App möglich sind:

fun Rates.computeMessageCost(contact: Contact): Cost =
   when (contact) { // ERROR: 'when' expression must be exhaustive
       is Contact.PhoneCall -> phoneCallCost
       is Contact.TextMessage -> textMessageCost
   }

Dies ist eine große Hilfe sowohl beim Schreiben des Codes als auch bei der späteren Wartung. Wenn Sie später eine weitere Kontaktoption hinzufügen, können Sie sich darauf verlassen, dass der Compiler Sie daran erinnert, die verschiedenen Kontaktpräferenzen überall in Ihrem Code zu berücksichtigen.

Vor Kotlin 1.6 wurde jedoch die when-Anweisung im folgenden Code erfolgreich kompiliert, obwohl versäumt wurde, den Versand einer Nachricht per Instant Messaging zu implementieren:

fun sendAnnouncement(contact: Contact, announcement: Announcement) {
   when (contact) {
       is Contact.PhoneCall -> schedulePhoneCall(contact.number, announcement)
       is Contact.TextMessage -> sendTextMessage(contact.number, announcement)
   }
}

Das Problem wurde nur von einer nachrangigen IDE-Inspektion gemeldet, ohne irgendwelche Meldungen vom Compiler. Ab Kotlin 1.6 wird die folgende Compiler-Warnung ausgegeben:

Non-exhaustive 'when' statements on sealed class/interface will be prohibited in 1.7. Add an 'is InstantMessage' branch or 'else' branch instead.

In Kotlin 1.7 wird dies als Fehler gewertet, damit solche Fälle nicht übersehen werden können. Im Ticket KT-47709 finden Sie eine genauere Erläuterung der Änderung und ihrer Auswirkungen. 

Suspendierende Funktionen als Supertypen

Kotlin 1.6 stabilisiert die Unterstützung für die Implementierung von suspend-Funktionstypen als Superschnittstellen. Dies war eines der fehlenden Puzzleteile beim Design der Kotlin-Coroutinen. 

Bei der Gestaltung von Kotlin-APIs ist es idiomatisch, Funktionstypen zu akzeptieren, wenn das Verhalten von Bibliotheksfunktionen anpassbar sein soll. Beispielsweise verfügt die Schnittstelle Job der API kotlinx.coroutines über eine Memberfunktion, die ungefähr so aussieht:

fun invokeOnCompletion(handler: () -> Unit)

Sie können diese Funktion bequem mit Lambdas wie nachAbschlussAufrufen { etwasTun() } verwenden. Wenn Sie eine Klasse haben, die auf den Abschluss reagieren soll, können Sie Ihren Code vereinfachen und optimieren, indem Sie den Funktionstyp () -> Unit direkt in Ihrer Klasse implementieren, sodass Sie keine zusätzliche Lambdafunktion benötigen:

class MyCompletionHandler : () -> Unit {
   override fun invoke() { doSomething() }
}

Ab Kotlin 1.6 ist diese Optimierung mit suspendierenden Funktionen möglich. Nehmen wir an, Ihre API akzeptiert suspendierende Funktionstypen wie diese:

public fun launchOnClick(action: suspend () -> Unit) {}

In diesem Fall sind Sie nicht mehr darauf beschränkt, Lambdas und Referenzen auf suspendierende Funktionen an diesen Code zu übergeben. Sie können die entsprechenden suspendierenden Funktionstypen auch in einer Klasse implementieren:

class MyClickAction : suspend () -> Unit {
   override suspend fun invoke() { doSomething() }
}

Umwandlungen in suspendierende Typen

Kotlin 1.6 befördert die Umwandlungen von regulären zu suspendierenden Funktionstypen in den stabilen Status. Wenn bei einem Parameter ein suspendierender Typ erwartet wird, können Sie jetzt einen geeigneten regulären Funktionstyp übergeben. Der Compiler führt die Umwandlung automatisch durch.

Dadurch wird eine kleine, aber störende Inkonsistenz beim Zusammenspiel zwischen regulären und suspendierenden Funktionen in Kotlin behoben. Wenn Sie eine Funktion höherer Ordnung haben, die eine suspendierende Funktion akzeptiert (Beispiel: ein collect-Aufruf auf einem Kotlin-Flow), müssen Sie zum Aufrufen nicht unbedingt ein Lambda wie im folgenden Code verwenden:

flow.collect { processItem(it) }
fun processItem(item: Item) { /* ... */ }

Stattdessen könnte es praktischer sein, eine Referenz auf die Funktion processItem an den collect-Aufruf zu übergeben – die Wirkung ist identisch.

flow.collect(::processItem)

Sie könnten dann eine Referenz auf Ihre Verarbeitungsfunktion in eine Variable extrahieren, um das Verhalten in Ihrem Code entsprechend anzupassen. Allerdings funktionierte dies in den Kotlin-Versionen vor 1.6 nicht. Dies liegt daran, dass Sie eine reguläre Funktion als Parameter übergeben, wo ein suspendierender Typ erwartet wird:

val processingFunction = ::processItem
flow.collect(processingFunction) // ERROR: Type mismatch

Der obige Code wird in Kotlin 1.6 einwandfrei kompiliert und funktioniert auch wie erwartet.

Verbesserte Typinferenz für rekursive generische Typen

Ab 1.6.0 kann der Kotlin-Compiler standardmäßig ein Typargument basierend nur auf den oberen Grenzen des entsprechenden Typparameters ableiten, wenn dieser ein rekursiver generischer Typ ist. Dies ermöglicht verschiedene Patterns mit rekursiven generischen Typen, die in Java häufig für Builder-APIs verwendet werden.

// Vor 1.5.30
val containerA = PostgreSQLContainer<nothing>(DockerImageName.parse("postgres:13-alpine")).apply {
    withDatabaseName("db")
    withUsername("user")
    withPassword("password")
    withInitScript("sql/schema.sql")
}

// Mit Compileroption in 1.5.30 oder standardmäßig ab 1.6.0
val containerB = PostgreSQLContainer(DockerImageName.parse("postgres:13-alpine"))
    .withDatabaseName("db")
    .withUsername("user")
    .withPassword("password")
    .withInitScript("sql/schema.sql")

Verbesserte Builder-Inferenz

In Kotlin 1.5.30 wurde die Compileroption -Xunrestricted-builder-inference eingeführt, um innerhalb von Builder-Lambdas auf Typinformationen über einen Builder-Aufruf zugreifen zu können. Konkret bedeutet dies die Möglichkeit, Aufrufe zu tätigen, die eine Instanz eines noch nicht bekannten Typs zurückgeben, z. B. get() in einem buildList()-Lambda.

Ab 1.6.0 müssen Sie nicht die Option -Xunrestricted-builder-inference angeben, um diese bisher unzulässigen Aufrufe zu tätigen. Mit der Compileroption -Xenable-builder-inference können Sie jetzt ohne Verwendung der Annotation @BuilderInference Ihre eigenen generischen Builder schreiben und Builder-Inferenzen automatisch aktivieren, wenn die reguläre Typinferenz keine Typinformationen ergibt.

Längere Unterstützung früherer API-Versionen

Ab Kotlin 1.6.0 können Sie bei der Entwicklung außer der aktuellen stabilen API-Version drei frühere Versionen (anstelle der bisherigen zwei) verwenden. Derzeit stehen Ihnen also die API-Versionen 1.3, 1.4, 1.5 und 1.6 zur Verfügung.

Kotlin/JVM

Wiederholbare Annotationen mit Laufzeiterhaltung. Wie Java 8 kennt auch Kotlin wiederholbare Annotationen. Ab Kotlin 1.6 ist diese Funktion Java-kompatibel. @kotlin.annotation.Repeatable akzeptiert jetzt jede Retention-Angabe und macht die Annotation sowohl in Kotlin als auch in Java wiederholbar. Außerdem werden wiederholbare Java-Annotationen jetzt auch auf Kotlin-Seite unterstützt.

Kotlin/Native

Ab sofort können Sie die experimentelle Version des neuen Kotlin/Native-Speichermanagers ausprobieren. Mit diesem Feature kommen wir unserem Ziel einer konsistenten Entwicklererfahrung bei Multiplattform-Projekten näher. Der neue Speichermanager hebt die bestehenden Beschränkungen für die gemeinsame Objektnutzung zwischen Threads auf und bietet Primitive für die nebenläufige Programmierung, die sicher sind, Lecks vermeiden und keine spezielle Verwaltung oder Annotationen von Seiten des Entwicklungsteams erfordern.

Sie können Xcode unbesorgt aktualisieren und an Ihren Kotlin-Projekten weiterarbeiten, da Kotlin/Native jetzt Xcode 13 unterstützt. 

Kompilieren von Windows-Zielen auf beliebigen Hosts. Sie können die Windows-Ziele mingwX64 und mingwX86 auf jedem Host kompilieren, der Kotlin/Native unterstützt.

Wir haben die LLVM-Abhängigkeit, die Kotlin/Native unter der Haube verwendet, überarbeitet. Neben einer auf 11.1.0 aktualisierten LLVM-Version und einer verringerten Größe der Abhängigkeiten bietet dies einige weitere Vorteile.

Vereinheitlichung der Compiler-Plugin-ABI mit den JVM- und JS-IR-Backends. Jetzt kann das Gradle-Plugin für Kotlin Multiplatform das einbettbare Compiler-Jar, das für die JVM- und JS-IR-Backends verwendet wird, auch für Kotlin/Native verwenden. Sie können jetzt dieselben Compiler-Plugin-Artefakte für die Native-Plattform und andere unterstützte Plattformen verwenden.

Kotlin/JS

Für Buildvorgänge auf einem Server ohne Internetverbindung können Sie jetzt das Herunterladen von Node.js und Yarn für Kotlin/JS-Projekte deaktivieren und die auf dem Host vorinstallierten Instanzen verwenden.

Kover

Die genaue Ermittlung der Code-Coverage war vom ersten Kotlin-Release an eine Herausforderung. Einige großartige Tools wie JaCoCo verarbeiten Kotlin-Code, sind jedoch nicht vollständig in unsere Gradle-Toolchain und unsere Multiplattform-Projekte integriert. In dieser Kotlin-Version haben wir begonnen, dieses Problem anzupacken. Unser neues Gradle-Plugin Kover ermittelt die Code-Coverage von Kotlin-Code, der mit dem Kotlin/JVM-Compiler kompiliert wurde. Das Tool befindet es sich in einer frühen Entwicklungsphase und gilt als experimentell – wir freuen uns auf Ihr Feedback in GitHub.

Sehen Sie sich das Kover-Video an, um mehr zu erfahren.

Standardbibliothek

Kotlin 1.6.0 schließt drei Einträge auf der stdlib-Roadmap: kein !! nach readLine(), stabiler Status für typeOf() und eine stabile Duration-API. Außerdem erhalten die folgenden stdlib-Elemente den Status Stable:

  • Collection-Builder
  • Bit-Rotation bei Ganzzahlen
  • Regex-Funktion zum Aufteilen von Zeichenfolgen in Sequenzen

Außerdem können Sie jetzt compareTo mit Infixnotation verwenden, und wir haben an der Konsistenz zwischen den replace()-Funktionen in JVM und JS gearbeitet.

Neue Funktionen für die Standardeingabe

In Kotlin 1.6.0 ist der Nicht-Null-Assertion-Operator !! nach dem Lesen einer Zeile von der Standardeingabe nicht mehr erforderlich. Dies vereinfacht den Einstieg für Neulinge und erleichtert das Unterrichten von Kotlin.

Wir bieten die folgenden neuen Möglichkeiten für das Lesen von der Konsole:

  • readln() löst eine Ausnahme aus, wenn das Dateiende erreicht wurde. Sie können diese Funktion verwenden, statt das Ergebnis von readLine() mit dem Operator !! auf null zu überprüfen.
  • Die neue Funktion readlnOrNull() ist eine Alternative, die null zurückgibt. Sie verhält sich also wie bisher readLine() – nur dass die Funktion jetzt einen passenderen Namen hat.

Diese Funktionen sind für JVM und Native verfügbar. Sie sind analog zu println() benannt – für Neueinsteiger*innen ist dies besonders hilfreich.

fun main() {
    println("Input two integer numbers each on a separate line")
    val num1 = readln().toInt()
    val num2 = readln().toInt()
    println("The sum of $num1 and $num2 is ${num1 + num2}")
}

Stabile Duration-API

Mithilfe Ihres Feedbacks haben wir die API für Zeiträume stabilisiert und den entsprechenden Roadmap-Eintrag geschlossen.

Neben einer besser lesbaren Ausgabe von Duration.toString() und neuen Funktionen zum Parsen von Durations aus Strings, die in 1.5.30 als Preview verfügbar waren, hat die Duration-API folgende Änderungen erfahren:

  • Die days-Komponente der Funktion toComponents ist jetzt vom Typ Long statt Int, um das Abschneiden von Werten zu vermeiden.
  • Die Enum DurationUnit ist kein Typalias mehr. Es gibt keine Fälle, in denen es als Typalias für Java.util.concurrent.TimeUnit in der JVM verwendet wird.
  • Als Wunsch der Community sind Erweiterungseigenschaften wie Int.seconds wieder da. Um ihre Anwendbarkeit einzuschränken, sind sie nur im Companion-Objekt der Klasse Duration verfügbar.
import kotlin.time.Duration.Companion.seconds

fun main() {
//sampleStart
    val duration = 5000.seconds
    println("There are ${duration.inWholeMinutes} minutes in ${duration.inWholeHours} hours")
//sampleEnd
}

Stabiles typeOf()

In Kotlin 1.6.0 hat typeOf() den Status Stable. Der entsprechende Roadmap-Eintrag wurde geschlossen. Seit 1.3.40 war typeOf() auf der JVM-Plattform als experimentelle API verfügbar. Jetzt ist die Funktion auf jeder Kotlin-Plattform verfügbar und gibt einen beliebigen Kotlin-Typ, den der Compiler ableiten kann, als KType zurück. 

inline fun <reified T> renderType(): String {
    val type = typeOf<T>()
    return type.toString()
}

fun main() {
    val fromExplicitType = typeOf<Int>()
    val fromReifiedType = renderType<List<Int>>()
}

Stabile Collection-Builder

Kotlin 1.6.0 befördert Collection-Builder-Funktionen wie buildMap(), buildList() und buildSet() in den Stable-Status. Von den Buildern zurückgegebene Collections sind jetzt im schreibgeschützten Zustand serialisierbar.

Stabile Bitrotation für Ganzzahlen

Die Funktionen rotateLeft() und rotateRight(), die die binäre Darstellung einer Zahl um die angegebene Anzahl von Bits nach links oder rechts rotieren, haben ab Kotlin 1.6.0 den Status Stable.

fun main() {
//sampleStart
    val number: Short = 0b10001
    println(number.rotateRight(2).toString(radix = 2)) // 100000000000100
    println(number.rotateLeft(2).toString(radix = 2))  // 1000100
//sampleEnd
}

Stabile Regex-Funktion zum Aufteilen von Zeichenfolgen in Sequenzen

Eine weitere seit Kotlin 1.6.0 stabile Funktion ist splitToSequence() zum Aufteilen einer Zeichenfolge in eine Sequenz mithilfe eines regulären Ausdrucks.

fun main(){
//sampleStart
    val colorsText = "green, red , brown&blue, orange, pink&green"
    val regex = "[,\s]+".toRegex()
    val mixedColor = regex.splitToSequence(colorsText)
        .onEach { println(it) }
        .firstOrNull { it.contains('&') }
    println(mixedColor) // "brown&blue"
//sampleEnd
}

CompareTo in Infixnotation

Die Funktion Comparable.compareTo zum Vergleichen von zwei Objekten kann jetzt in Infixnotation aufgerufen werden: 

class WrappedText(val text: String) : Comparable<WrappedText> {
    override fun compareTo(other: WrappedText): Int =
        this.text compareTo other.text
}

Einheitlichkeit zwischen JVM und JS bei replace() und replaceFirst()

Vor Kotlin 1.6.0 verhielten sich die Regex-Funktionen replace() und replaceFirst() auf JVM und JS unterschiedlich, wenn die Ersetzungszeichenfolge eine Gruppenreferenz enthielt. Das Verhalten von Kotlin/JS wurde an das Verhalten der JVM angepasst.

Kompatibilität

Wie bei allen Feature-Releases werden mit Kotlin 1.6.0 einige Deprecation-Zyklen für zuvor angekündigte Änderungen abgeschlossen. Alle diese Fälle wurden vom Sprachkomitee sorgfältig geprüft und sind im Kompatibilitätsleitfaden für Kotlin 1.6 aufgeführt. Sie können diese Änderungen auch in YouTrack einsehen.

Installation von Kotlin 1.6.0

Wenn Sie IntelliJ IDEA oder Android Studio verwenden, schlägt Ihnen die IDE automatisch die Aktualisierung auf Kotlin 1.6.0 vor. Alternativ ist auch eine manuelle Aktualisierung gemäß dieser Anleitung möglich.

Sie können die neuesten Versionen dieser IDEs herunterladen, um eine umfassende Kotlin-Unterstützung zu erhalten:

  • IntelliJ IDEA – zur Entwicklung von Kotlin-Anwendungen für verschiedene Plattformen.
  • Android Studio – zur Entwicklung von Android-Apps und plattformübergreifenden Mobilanwendungen.

Achten Sie darauf, auch die kotlinx-Bibliotheken auf die kompatiblen Versionen zu aktualisieren und die Kotlin-Version 1.6.0 in den Build-Skripten Ihrer bestehenden Projekte anzugeben.

Den Befehlszeilen-Compiler können Sie bei Bedarf von der GitHub-Release-Seite herunterladen.

Hilfe bei Problemen:

Bleiben Sie stets auf dem neuesten Stand der Kotlin-Entwicklung! Abonnieren Sie unsere Kotlin-Updates, indem Sie das Formular rechts neben diesem Beitrag ausfüllen.

Weitere Informationen zum Lesen und Ansehen

Liste der aktivsten Ticketersteller in YouTrack 

Ryan Nett (48 Tickets), Zac Sweers (22 Tickets), Tianyu Geng (18 Tickets), zhelenskiy (18 Tickets), Thodoris Sotiropoulos (15 Tickets), AndroidDeveloperLB (14 Tickets), Morgan, Bartholomew (14 Tickets), Mikhail Naftal (14 Tickets), Louis CAD (12 Tickets), Philip Wedemann (12 Tickets), Victor Turansky (11 Tickets), Igor Wojda (11 Tickets), Martin Bonnin (11 Tickets), Iaroslav Postovalov (11 Tickets), Cedric (10 Tickets), (9 Tickets), Ben Woodworth (8 Tickets), Tianyi Guan (8 Tickets), Chris Povirk (8 Tickets), Alon Albert (8 Tickets).

Externe Mitwirkende

Wir bedanken uns bei allen Mitwirkenden, die Pull-Requests zu dieser Version beigetragen haben: Pyos, Tianyu Geng, Jinseong Jeon, Steven Schäfer, Mark Punzalan, Hung Nguyen, Mads Ager, Ting-Yuan Huang, Matthew Gharrity, Ivan Gavrilovic, Xin Wang, ov7a, Jiaxiang Chen, Yigit Boyar, Bingran, bracadabra, Steffi Stumpos, Andrey Kuleshov.

Autorin des Original-Blogposts:

Sergiy Rogalin

Alina Grebenkina