Backend, Mobile

Transformacja Javy do Kotlina. Proste przykłady

W tym wpisie nie pokażę Ci jak zainstalować Kotlina, ale przedstawię kilka podstawowych fragmentów kodów Javowych oraz ich transformację do kodu Kotlinowego. Zobaczysz, jak bardzo niektóre rzeczy można uprościć uzyskując taki sam lub zbliżony efekt końcowy. Artykuł przyda się osobom, które chcą wdrożyć Kotlina do projektu i zaczynają przepisywać niektóre funkcjonalności z Javy.

Definiowanie zmiennych

Na początek napiszemy jeden prosty program w dwóch językach. Stworzymy dwie zmienne, które są integerami. Jedna nie może zmienić wartości, druga może, nawet na nulla (to jest bardzo ważne w kontekście Kotlina!). Zacznijmy od Javy.

Java:

public static void main(String[] args) {
    final Integer finalInt = 66;
    System.out.println("finalInt. Int type? " + Integer.class.isInstance(finalInt) + ". Value: " + finalInt);
    // finalInt = 67; nie zadziała

    Integer notFinalInt = 70;
    notFinalInt = 71;
    System.out.println("notFinalInt. Int type? " + Integer.class.isInstance(notFinalInt) + ". Value: " + notFinalInt);
    // notFinalInt = "71"; nie zadziała

    notFinalInt = null;
}

Jak widzimy, aby zmienna w Javie nie mogła się zmienić musimy do deklaracji dodać słówko kluczowe final. Dla pewności jeszcze chcemy sprawdzić typ zmiennej finalInt. Definitywnie jest tam Integer. Druga zmienna (notFinalInt) może zmienić wartość. Nadal jest to typ Integer. Oczywiście, Java jest językiem silnie typowanym, czyli już zadeklarowanego typu zmiennej nie zmienimy, a więc do zmiennej typu Integer nie przypiszemy “71” jako String. Z przypisaniem wartości null problemu w Javie nie ma. Możemy go przypisać gdziekolwiek (pod warunkiem, że zmienna nie jest finalna).

Teraz przejdźmy do Kotlina.

Kotlin:

fun main(args: Array<String>){
    val finalInt = 66
    println("finalInt. Int type? ${finalInt is Int}. Value: $finalInt")
    // finalInt = 67 nie zadziała

    var notFinalInt = 70
    notFinalInt = 71
    println("notFinalInt. Int type? ${notFinalInt is Int}. Value: $notFinalInt")
    // notFinalInt = "71" // nie zadziała
    // notFinalInt = null // nie zadziała
}

Tak. Deklaracja val finalInt = 66 w Kotlinie jest równa final Integer finalInt = 66; w Javie. Dlaczego? Przede wszystkim, w Kotlinie nie ma słówka final. Przy deklaracje zmiennej używamy albo słówka var (variable) albo val (value). W pierwszym przypadku zmienna nie jest finalna, w drugim jest. Duży skrót. Ponadto, nie widać nigdzie deklaracji typu. Dlaczego, skoro Kotlin też jest silnie typowany? Ponieważ występuje tu coś takiego jak “type interference”. Jeżeli Kotlin jest w stanie wywnioskować jaki jest typ zmiennej, to od razu typ jest zadeklarowany bez pisania go wprost!

W następnej linijce sprawdzamy wartość i czy faktycznie jest to Int. W Kotlinie typ można sprawdzić poprzez użycie słówka is. Czytelnie, prawda? Może Cię zaskoczyć też ten zapis wiadomości, jakieś dolary itd. Jest to tzw “string interpolation”. Nie musimy wszędzie robić plusów, możemy nazwę zmiennej poprzedzić dolarem i w tym miejscu zostanie wstawiona jego wartość do łańcucha. Jeżeli mamy natomiast wywołanie funkcji, odwołanie do pola lub coś bardziej złożonego to już musimy to otoczyć “wąsami”. Następnie, wykomentowane przypisanie wartości 67 nie zadziała, gdyż zadeklarowaliśmy finalInt jako zmienną finalną.

Dalej tworzymy kolejną zmienną, notFinalInt, tym razem poprzedzoną słówkiem var. Typ również zostanie poprawnie wywnioskowany (wypisanie na dole to potwierdzi), a nadpisanie wartości nie stanowi problemu. Przypisanie wartości “71” nie zadziała, co potwierdza, że typ jest wcześniej określony (próbujemy przypisać Stringa do Inta, nie może być!). Natomiast, przypisanie wartości null o dziwo też nie zadziała, co może być dość dziwne. Dlaczego? O tym zaraz.

Ochrona przed nullem

Chyba nie ma programisty w świecie Javy, który by nie doświadczył czegoś takiego jak NullPointerException. Bardzo irytujący wyjątek, często ciężki do znalezienia, szczególnie gdy mamy ciąg wywołań funkcji w jednej linijce. Prosty przykład z Javy jak się przed tym zazwyczaj zabezpieczamy.

Java:

public static void main(String[] args) {
    List<String> stringsList = new ArrayList<String>();
    stringsList.add("Ala");

    if (stringsList != null) {
        System.out.println("List size: " + stringsList.size());
    } else {
        System.out.println("List size: null");
    }

    stringsList = null;
    if (stringsList != null) {
        System.out.println("List size: " + stringsList.size());
    } else {
        System.out.println("List size: null");
    }
}

W kodzie powyżej chcemy sprawdzić rozmiar listy i wypisać komunikat. Jeżeli lista byłaby nullem to zobaczylibyśmy NullPointerException, program by się “wykrzaczył”. Musimy się zabezpieczać i sprawdzać za każdym razem czy lista jest różna od nulla, jak powyżej.

W Kotlinie domyślnie każdy obiekt jest typu nienullowego (chyba, że przypiszemy do niego od razu nulla). Jeżeli zmienna może być nullem to do definicji typu dodajemy pytajnik, a więc typ Int jest inny od typu Int?. Ten drugi może posiadać nulla, natomiast wiążą się z tym pewne zasady, które musimy przestrzegać. W przypadku tego pierwszego natomiast nie musimy się nullem przejmować. Sprawdźmy to w praktyce.

fun main(args: Array<String>){
    var stringList = listOf("Ala")
    println("List size: ${stringList.size}")
    // stringList = null // nie zadziala
}

Nie musimy się przejmować tutaj nullem. Mamy w tym przypadku pewność, że NPE nie zostanie rzucony bo od razu wywnioskowany typ przez Kotlina to będzie List. A jak by to wyglądało gdyby typ mógł być nullem?

fun main(args: Array<String>){
    var stringList: List<String>? = listOf("Ala")
    println("List size: ${stringList?.size}")

    stringList = null
    println("List size: ${stringList?.size}")
}

Lista została zdefiniowana jako nullable, gdyż potem będę chciał do niej przypisać nulla (pytajnik po nazwie typu). Początkowo jednak lista nie posiada nulla. Mimo to sprawdzenie rozmiaru jest inne niż w poprzednim przypadku. Odwołanie do pola size w tym przypadku jest poprzedzone pytajnikiem. W Kotlinie dla typów nullowych jest to wymagane, a więc trzeba trzymać dyscyplinę przy wywołaniach i odwoływaniu się do zmiennej za każdym razem! Kod zadziała praktycznie jak powyżej, rozmiar zostanie wypisany, a różnica jest tylko w jednym znaku. Zagwozdka zaczyna się potem, jest sprawdzany rozmiar nulla. Kod natomiast zachowa się jak przykład w Javie, zostanie wypisany tekst “null”, natomiast żaden NPE nie zostanie rzucony mimo braku jakiegokolwiek ifa. Jesteśmy bezpieczni!

Oba kody zachowują się praktycznie tak samo, natomiast po raz kolejny zapis w Kotlinie jest bardziej przejrzysty. Ponadto, widać jak ważne jest projektowanie kodu na wczesnym etapie. Wyrzucając nulle z aplikacji nasz kod jest wiele schludniejszy, a zmartwień coraz mniej. Jeżeli jednak w kotlinowym kodzie mamy świadomość, że może nam gdzieś wartość null wskoczyć, to musimy się o to troszczyć w przypadku danego obiektu za każdym razem! Chyba nie muszę mówić, które podejście jest lepsze i dlaczego to bez nulli?

Definiowanie funkcji

Teraz zobaczymy jak zdefiniować dwie proste funkcje w Javie oraz jej odpowiedniki w Kotlinie. Jedna funkcja będzie zwracać wartość 42, natomiast druga będzie robić to samo oraz wypisze odpowiedni komunikat.

Java:

class ClassWithFunctions {
    public static int fourtyTwo() {
        return 42;
    }

    public static int fourtyTwoWithMsg() {
        System.out.println("I am fourty two!");
        return 42;
    }
}

public static void main(String[] args) {
    System.out.println(ClassWithFunctions.fourtyTwo());
    System.out.println(ClassWithFunctions.fourtyTwoWithMsg());
}

W Javie do stworzenia funkcji potrzebujemy klasy. Funkcje zostały zadeklarowane jako statyczne, dzięki czemu nie musimy tworzyć obiektu typu ClassWithFunctions. Dwie proste funkcje, publiczne, statyczne, ze zdefiniowanym typem jaki zwracają. Odpowiednik w Kotlinie:

Kotlin:

fun fourtyTwo() = 42

fun fourtyTwoWithMsg(): Int {
    println("I am fourty two!")
    return 42
}

fun main(args: Array<String>) {
    println(fourtyTwo())
    println(fourtyTwoWithMsg())
}

Już na pierwszy rzut oka widać ogromne różnice! Jak już wcześniej pewnie zauważyłeś, funkcje w Kotlinie są poprzedzane wymaganym słówkiem kluczowym fun. Co ważne, funkcje domyślnie są finalne oraz publiczne! Jeżeli chcemy, by funkcja finalna nie była, wystarczy dodać słówko open. Kolejna duża rzecz, funkcje nie muszą być deklarowane w klasie! Możemy je robić jako funkcje top-level w pliku, możemy ich również potem używać w innych miejscach. Przykładem jest używana funkcja listOf przeze mnie wcześniej, też to jest funkcja top-level. Funkcja może być także deklarowana w innej funkcji, jest wtedy dostępna tylko w niej. Bardzo przydatne, gdy mamy jedną funkcję publiczną oraz wiele prywatnych używanych tylko w tej jednej publicznej.

Funkcje w Kotlinie mają również wiele innych mocy. Na przykład funkcja fourtyTwo jest funkcją jednolinijkową, która tylko zwraca wartość 42. Możemy więc zrobić coś na wzór przypisania wartości do zmiennej. Jest to tzw. “expression body”. Wartość 42 zostanie zwrócona tak samo. Różnica jest taka, że nie ma wąsów, słówka return nie używamy, a także deklaracja typu nie jest wymagana, Kotlin się domyśli. Ale to schludnie wygląda!

W drugiej funkcji już typ deklarujemy na końcu definicji, jak przy zmiennej. Dlaczego? Domyślnie funkcje w Kotlinie zwracają Unit, jest to odpowiednik void w Javie. Jeżeli nie zdefiniujemy typu zwracanego przez “normalną” funkcję to kompilator zobaczy tam Unit. Przyjęta praktyka w Kotlinie jest, że gdy chcemy zwrócić Unit to nie definiujemy tego, Kotlin wie. Tutaj jednak chcemy Int, więc jest to odpowiednio określone. Reszta funkcji nie różni się od tej w Javie, wypisanie oraz return.

W funkcji main wołam obie funkcje, bez problemu, wynik jest jak powyżej. Nie potrzebuję odwoływać się do pliku albo do klasy. Wiele mniej roboty dla takiego prostego programiku.

Podsumowanie

To tyle na dziś. Kilka prostych przykładów, które już pokazują moc Kotlina. Często mniej kodu jest potrzebne do stworzenia niektórych rzeczy, często idzie za tym przejrzystość. Jeszcze raz gorąco zachęcam do sprawdzenia na własnej skórze przykładów Kotlinowych oraz modyfikację ich trochę.


najwięcej ofert html

Artykuł został pierwotnie opublikowany na blogu simplecoding.pl, jest częścią serii artykułów o transformacji Javy do Kotlina. Zdjęcie główne artykułu powstało na podstawie pexels.com.

Full-stack Java Developer w firmie Metrosoft

Nadal rozwija swoje umiejętności jako programista poprzez naukę nowych technologi oraz jako speaker poprzez dzielenie się swoją wiedzą z innymi programistami.

Podobne artykuły

[wpdevart_facebook_comment curent_url="https://justjoin.it/blog/transformacja-javy-kotlina-proste-przyklady" order_type="social" width="100%" count_of_comments="8" ]