Kotlin wurde zunächst als sichere und prägnante Alternative zu Java entworfen. Eine seiner Stärken ist der ausgeklügelte Mechanismus der Typerkennung (Type Inference), der es ermöglicht, weniger ausführlichen Code zu schreiben, ohne die Typisierung zu verlieren. Die Typerkennung wurde von funktionalen Programmiersprachen (z.B. Scala und Haskell) sowie von modernen Trends beim Design statisch typisierter Sprachen inspiriert.
In Java und anderen statischen Sprachen ist es erforderlich, die Typen explizit anzugeben, was zu Redundanz im Code führt. Allerdings kann das Fehlen von expliziten Typen das Verständnis des Codes erschweren und zu nicht offensichtlichen Fehlern führen, wenn die Typerkennung nicht korrekt funktioniert.
In Kotlin kann der Compiler oft den Typ einer Variablen oder eines Ausdrucks aus dem Kontext heraus selbst bestimmen. Dies funktioniert für Variablen, Rückgabewerte von Funktionen und innerhalb von Ausdrücken mit Lambdas. Es gibt jedoch Situationen, in denen der Compiler eine explizite Angabe des Typs verlangt – beispielsweise beim Deklarieren einer Funktion ohne Rückgabewert innerhalb einer Klasse ('fun doSomething()') oder wenn die Ausdrücke mehrdeutig sind.
Beispielcode:
val a = 42 // Int val s = "hello" // String fun sum(x: Int, y: Int) = x + y // Rückgabetyp Int wird automatisch abgeleitet val list = listOf(1, 2, 3) // List<Int> // Explizite Typangabe ist erforderlich, wenn der Typ nicht abgeleitet werden kann val emptyList: List<String> = emptyList() // sonst wird es List<Nothing>
Wesentliche Merkmale:
Warum kann man den Typ nach dem Doppelpunkt nicht immer weglassen, z.B. für Klassen-Eigenschaften?
Für Eigenschaften, die nicht am Deklarationsort initialisiert werden (z.B. über einen Getter oder im init-Block), kann der Compiler den Typ nicht automatisch ableiten, da er den Initialisierer nicht sieht.
class User { val fullName: String // Typ muss angegeben werden, sonst Fehler get() = "name" }
Welchen Typ hat die Variable, wenn man emptyList() ohne expliziten Typ verwendet?
Der Typ wird als List<Nothing> abgeleitet, was das Ergebnis praktisch nutzlos macht.
val list = emptyList() // List<Nothing>
Wann funktioniert die Typerkennung nicht bei Funktionsparametern?
In der Funktionssignatur ist es immer erforderlich, die Typen der Parameter explizit anzugeben, andernfalls gibt der Compiler einen Fehler aus.
// Fehler: // fun foo(x) = x * 2 // Richtig: fun foo(x: Int) = x * 2
Ein Entwickler verwendet emptyList() zur Rückgabe eines Wertes aus einer API-Funktion, ohne den Typ explizit anzugeben. Infolgedessen hat er den Typ List<Nothing>, was zu Problemen bei der Arbeit mit dieser API führt.
Vorteile:
Ein Entwickler gibt immer explizit den Typ an, wenn er mit leeren Sammlungen arbeitet und wenn dies die Lesbarkeit verbessert, und verlässt sich in anderen Fällen auf die Typerkennung des Compilers.
Vorteile: