Kotlin został zaprojektowany jako bezpieczna i zwięzła alternatywa dla Javy. Jedną z jego mocnych stron jest rozwinięty mechanizm wnioskowania typów (type inference), który pozwala pisać mniej obszerne kody bez utraty typizacji. Wnioskowanie typów inspirowano językami funkcyjnymi (takimi jak Scala i Haskell) oraz nowoczesnymi trendami projektowania języków o statycznych typach.
W Javie i innych językach statycznych wymaga się jawnego wskazywania typów, co prowadzi do nadmiarowości kodu. Jednak brak jawnych typów może utrudniać zrozumienie kodu i prowadzić do nieoczywistych błędów, jeśli wnioskowanie typów nie zadziała poprawnie.
W Kotlinie kompilator często może samodzielnie określić typ zmiennej lub wyrażenia na podstawie kontekstu. Działa to dla zmiennych, zwracanych wartości funkcji i wewnątrz wyrażeń z lambdami. Istnieją jednak sytuacje, w których kompilator wymaga jawnego określenia typu — na przykład przy deklaracji funkcji bez zwracanej wartości wewnątrz klasy ('fun doSomething()') lub gdy wyrażenia są niejednoznaczne.
Przykładowy kod:
val a = 42 // Int val s = "hello" // String fun sum(x: Int, y: Int) = x + y // zwracany typ Int jest automatycznie wnioskowany val list = listOf(1, 2, 3) // List<Int> // Jawne wskazanie typu jest konieczne, jeśli wartość nie może być wnioskowana val emptyList: List<String> = emptyList() // w przeciwnym razie będzie List<Nothing>
Kluczowe cechy:
Dlaczego nie można zawsze pominąć typu po dwukropku, na przykład dla właściwości klasy?
Dla właściwości, które są inicjalizowane nie w miejscu deklaracji (na przykład przez getter lub w bloku init), kompilator nie może automatycznie wnioskować typu, ponieważ nie widzi inicjalizatora.
class User { val fullName: String // Typ musi być określony, w przeciwnym razie błąd get() = "name" }
Jaki typ będzie miała zmienna, jeśli użyjemy emptyList() bez jawnego typu?
Będzie wnioskowany typ List<Nothing>, co czyni wynik praktycznie bezużytecznym.
val list = emptyList() // List<Nothing>
Kiedy wnioskowanie typów nie działa z parametrami funkcji?
W sygnaturze funkcji zawsze należy jawnie wskazać typy parametrów, w przeciwnym razie kompilator zgłosi błąd.
// Błąd: // fun foo(x) = x * 2 // Poprawnie: fun foo(x: Int) = x * 2
Programista używa emptyList() do zwrócenia wartości z funkcji API, nie wskazując typu jawnie. W wyniku uzyskuje typ List<Nothing>, co powoduje problemy podczas pracy z tym API.
Zalety:
Programista zawsze jawnie wskazuje typ przy pracy z pustymi kolekcjami i tam, gdzie to poprawia czytelność, a w innych przypadkach polega na wnioskowaniu typów przez kompilator.
Zalety: