Slide 1

Slide 1 text

Kotlin 2.1 Language Updates

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Language Features •Suppor t for requiring opt-in to extend APIs •Non-local break and continue •Multi-dollar interpolation •Guard conditions in when-with-subject expressions •Improved exhaustiveness checks for when expressions with sealed classes

Slide 4

Slide 4 text

Opt-in requirements for subclasses

Slide 5

Slide 5 text

Opt-in requirements for subclasses @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = "Interfaces in this library are experimental" ) annotation class UnstableApi() @SubclassOptInRequired(UnstableApi :: class) interface CoreLibraryApi interface MyImplementation : CoreLibraryApi Library User code

Slide 6

Slide 6 text

Opt-in requirements for subclasses @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = "Interfaces in this library are experimental" ) annotation class UnstableApi() @SubclassOptInRequired(UnstableApi :: class) interface CoreLibraryApi interface MyImplementation : CoreLibraryApi Library User code

Slide 7

Slide 7 text

Opt-in requirements for subclasses @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = "Interfaces in this library are experimental" ) annotation class UnstableApi() @SubclassOptInRequired(UnstableApi :: class) interface CoreLibraryApi @OptIn(UnstableApi :: class) interface MyImplementation : CoreLibraryApi Library User code

Slide 8

Slide 8 text

Local break and continue in lambdas

Slide 9

Slide 9 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { val reader = file.bufferedReader(Charsets.UTF_8) val line = reader.readLine() if (line.isNullOrEmpty()) continue else add(line) } } "Read the first line from each file in the list"

Slide 10

Slide 10 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { val reader = file.bufferedReader(Charsets.UTF_8) val line = reader.readLine() if (line.isNullOrEmpty()) continue else add(line) } }

Slide 11

Slide 11 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { val reader = file.bufferedReader(Charsets.UTF_8) val line = reader.readLine() if (line.isNullOrEmpty()) continue else add(line) } }

Slide 12

Slide 12 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { val reader = file.bufferedReader(Charsets.UTF_8) val line = reader.readLine() if (line.isNullOrEmpty()) continue else add(line) } }

Slide 13

Slide 13 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { val reader = file.bufferedReader(Charsets.UTF_8) val line = reader.readLine() if (line.isNullOrEmpty()) continue else add(line) } } Realize, that we need to use the resources properly

Slide 14

Slide 14 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { file.bufferedReader(Charsets.UTF_8).use { reader -> val line = reader.readLine() // this is now a 'continue' inside a lambda if (line.isNullOrEmpty()) continue else add(line) } } }

Slide 15

Slide 15 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { file.bufferedReader(Charsets.UTF_8).use { reader -> val line = reader.readLine() // this is now a 'continue' inside a lambda if (line.isNullOrEmpty()) continue else add(line) } } }

Slide 16

Slide 16 text

Local break and continue in lambdas fun List.readFirstLines(): List = buildList { for (file in this@readFirstLines) { file.bufferedReader(Charsets.UTF_8).use { reader -> val line = reader.readLine() // this is now a 'continue' inside a lambda if (line.isNullOrEmpty()) continue else add(line) } } } -Xnon-local-break-continue

Slide 17

Slide 17 text

Multi-dollar interpolation

Slide 18

Slide 18 text

Multi-dollar interpolation val jsonSchema: String = """ { "$id": "https: // example.com/product.schema.json", }"""

Slide 19

Slide 19 text

Multi-dollar interpolation val jsonSchema: String = """ { "$id": "https: // example.com/product.schema.json", }""" Interpolation fails for "id"

Slide 20

Slide 20 text

Multi-dollar interpolation val jsonSchema: String = """ { "${'$'}id": "https: // example.com/product.schema.json", }""" A workaround

Slide 21

Slide 21 text

Multi-dollar interpolation val jsonSchema: String = $$""" { "$id": "https: // example.com/product.schema.json", }""" -Xmulti-dollar-interpolation Use $$ as a symbol sequence for interpolation

Slide 22

Slide 22 text

Multi-dollar interpolation val jsonSchema: String = $$""" { "$id": "https: // example.com/product.schema.json", "parameter": $$parameter" }""" val parameter = "value" Use $$ as a symbol sequence for interpolation

Slide 23

Slide 23 text

Multi-dollar interpolation val jsonSchema: String = $$$$$$$$$$$$$$$$$$$$$$""" { "$id": "https: // example.com/product.schema.json", "parameter": $$$$$$$$$$$$$$$$$$$$$$parameter" }""" val parameter = "value" :)

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Guard conditions in when-with-subject expressions

Slide 27

Slide 27 text

when { order is YearlySubscription && order.amount > 100 -> applyDiscount(order) order is MonthlySubscription -> startSubscription(order) order is OneTimeOrder -> processOrder(order) } val order = getOrder() Guard conditions in when-with-subject expressions

Slide 28

Slide 28 text

when { order is YearlySubscription && order.amount > 100 -> applyDiscount(order) order is MonthlySubscription -> startSubscription(order) order is OneTimeOrder -> processOrder(order) } val order = getOrder() Potentially a logical error Repetition is not nice Guard conditions in when-with-subject expressions

Slide 29

Slide 29 text

when { order is YearlySubscription && order.amount > 100 -> applyDiscount(order) order is YearlySubscription -> processSubscription(order) order is MonthlySubscription -> startSubscription(order) order is OneTimeOrder -> processOrder(order) } val order = getOrder() Guard conditions in when-with-subject expressions

Slide 30

Slide 30 text

when(order) { is YearlySubscription && order.amount > 100 -> applyDiscount(order) is YearlySubscription -> processSubscription(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() Guard conditions in when-with-subject expressions

Slide 31

Slide 31 text

when(order) { is YearlySubscription && order.amount > 100 -> applyDiscount(order) is YearlySubscription -> processSubscription(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() Error: expecting ' -> ' && Guard conditions in when-with-subject expressions

Slide 32

Slide 32 text

when(order) { is YearlySubscription && order.amount > 100 -> applyDiscount(order) is YearlySubscription -> processSubscription(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() if Guard conditions in when-with-subject expressions Guarded conditions: KEEP - 371

Slide 33

Slide 33 text

when(order) { is YearlySubscription if order.amount > 100 -> applyDiscount(order) is YearlySubscription -> processSubscription(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() Guard conditions in when-with-subject expressions

Slide 34

Slide 34 text

when(order) { is YearlySubscription -> processSubscription(order) is YearlySubscription if order.amount > 100 -> applyDiscount(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() The 'when' branch is never reachable Guard conditions in when-with-subject expressions

Slide 35

Slide 35 text

when(order) { is YearlySubscription if order.amount > 100 -> applyDiscount(order) is YearlySubscription -> processSubscription(order) is MonthlySubscription -> startSubscription(order) is OneTimeOrder -> processOrder(order) } val order = getOrder() Guard conditions in when-with-subject expressions

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Improved exhaustiveness checks for when expressions with sealed classes sealed class Result object Error: Result() class Success(val value: String): Result() fun render(result: T) = when (result) { Error -> "Error!" is Success -> result.value // Requires no else branch } Required else branch before 2.1

Slide 38

Slide 38 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict'

Slide 39

Slide 39 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict' import org.jspecify.annotations.*; public class SomeJavaClass { @NonNull public String foo() { ... } @Nullable public String bar() { ... } } Java

Slide 40

Slide 40 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict' import org.jspecify.annotations.*; public class SomeJavaClass { @NonNull public String foo() { ... } @Nullable public String bar() { ... } } Java Kotlin val sjc = SomeJavaClass() sjc.foo().length sjc.bar().length

Slide 41

Slide 41 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict' import org.jspecify.annotations.*; public class SomeJavaClass { @NonNull public String foo() { ... } @Nullable public String bar() { ... } } Java Kotlin val sjc = SomeJavaClass() sjc.foo().length sjc.bar().length

Slide 42

Slide 42 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict' import org.jspecify.annotations.*; public class SomeJavaClass { @NonNull public String foo() { ... } @Nullable public String bar() { ... } } Java Kotlin val sjc = SomeJavaClass() sjc.foo().length sjc.bar() ?. length Raises an error in the default strict mode because the result is nullable. To avoid the error, use ?.length instead

Slide 43

Slide 43 text

Change of JSpecify nullability mismatch diagnostics severity to 'strict' import org.jspecify.annotations.*; public class SomeJavaClass { @NonNull public String foo() { ... } @Nullable public String bar() { ... } } Java Kotlin val sjc = SomeJavaClass() sjc.foo().length sjc.bar() ?. length kotlin { compilerOptions { freeCompilerArgs.add(" -- Xnullability-annotations=strict") } } // gradle.build.kts / / ignore | warning | strict

Slide 44

Slide 44 text

https://kotlinlang.org