Sytuacja kobiet w IT w 2024 roku
23.07.20204 min

Mało znane, ale przydatne funkcje Kotlina

Poznaj nieznane, ale przydatne funkcje Kotlina i zobacz, jak wypadają w porównaniu z Javą.

Mało znane, ale przydatne funkcje Kotlina

Wielu z nas uczyło się Kotlina zaraz po Javie. Nauka była łatwa, ponieważ Kotlin jest bardzo do Javy podobny, poprawiając jednak niektóre rzeczy, które w tym starszym języku bolały (szczególnie w Java 6, która jest popularna w świecie Androida). Istnieje wiele artykułów o świetnych funkcjach Kotlina (np. o niemutowalności, radzeniu sobie z nullami, smart-cast, klasach danych itd). I bardzo dobrze, bo wyżej wymienione funkcje są rzeczywiście świetne.

Konieczność dodawania adnotacji do nulli, słowa kluczowego final, nadpisywanie metod equals/hashCode, tworzenie dodatkowych zmiennych lokalnych po sprawdzeniu typów to niepotrzebna praca, którą trzeba wykonać. Istnieją jednak pewne rzeczy w Kotlinie, które nie są aż tak znane, ale mogą się przydać. W tym artykule je omówimy i porównamy z Javą.

Mutowalność referencji do parametrów metod


Java 

W Javie referencje do parametrów metod są mutowalne. Oznacza to, że obiekt pod daną referencją można podmienić. Aby był on niemutowalny, to trzeba dodać słowo kluczowe final. Niezastępowanie takich referencji jest jednak dobrą praktyką, ponieważ taka akcja zwiększa złożoność kodu. Oznacza to, że zawsze należałoby rozważyć dodanie final do wszystkich parametrów, co zdecydowanie nie jest satysfakcjonujące.

public class A {

    void foo(String param) {
        param = "hello";
        System.out.println(param);
    }

    void fooFinal(final String param) {
        param = "hello"; // Cannot assign a value to final variable 'param'
    }
}


Jak widać w powyższym snippecie, błąd kompilacji otrzymujemy tylko wtedy, gdy dodamy słowo kluczowe final.


Kotlin

W przeciwieństwie do Javy, w Kotlinie wszystkie parametry są domyślnie niemutowalne i nie ma sposobu, aby uczynić je mutowalnymi. Jest to zgodne z ogólnym idiomem Kotlina, który polega na ograniczaniu dostępu, chyba że wyraźnie zadeklarowano inaczej (więc zamiast słowa kluczowego final, mamy tu słowo kluczowe open).

W tym konkretnym przypadku nie ma możliwości uczynienia tego parametru ani mutowalnym, ani otwartym. Nie ma zresztą takiej potrzeby.

class A {

    fun foo(param: String) {
       param = "hello" // Val cannot be reassigned
    }
}

Pakiet private vs protected

Kotlin i Java mają różne podejście do widoczności i modyfikatorów dostępu. Chociaż są one dobrze udokumentowane, to istnieje co najmniej jeden przypadek, o którym warto wspomnieć. Chodzi tutaj o słowo kluczowe protected.


Java

W Javie istnieją cztery modyfikatory dostępu: public, protected, package-private (wartość domyślna bez oddzielnego słowa kluczowego), private.

Zasady są proste:

  • privatejest dostępne w wnętrza klasy
  • package-privatejest dostępne, jak w private plus
  • protectedjest dostępne jako package-private plus dla wszystkich klas podrzędnych
  • publicjest dostępne z każdego miejsca


Ważną rzeczą do zapamiętania jest to, że protected jest dostępne z tego samego pakietu, a nie tylko z klas podrzędnych. Jeśli zatem mamy klasę z dwoma metodami, z których jedna jest protected a druga package-private, to:

public class B {

    void packagePrivateMethod() {
        System.out.println("package private method called");
    }

    protected void protectedMethod() {
        System.out.println("protected method called");
    }
}


Oraz jeśli mamy klasę A w tym samym pakiecie, to klasa ta będzie mogła otrzymać dostęp do obu metod z klasy B:

public class A {

    void foo() {
        final B b = new B();
        b.packagePrivateMethod();
        b.protectedMethod();
    }
}


Kotlin

W Kotlinie istnieją różne modyfikatory dostępu: public (domyślny, można go pominąć), protected, internal, private.

  • publicjest dostępne z każdego miejsca
  • protectedjest dostępne tylko z klasy podrzędnej
  • internaljest dostępne z całego modułu (nie tylko z pakietu, jest to „publiczne w module”)
  • privatejest dostępne wewnątrz pliku lub klasy


Jeśli mamy zatem klasę, która jest podobna do poprzedniego przykładu:

open class B {

    protected open fun protectedMethod() {
        println("protected method called")
    }

    internal open fun internalMethod() {
        println("internal method called")
    }
}


Wtedy klasa A, będąc w tym samym pakiecie, nie miałaby dostępu do metody chronionej:

class A {

    fun foo() {
        val b = B()

        b.internalMethod()
        b.protectedMethod() // Cannot access 'protectedMethod': it is protected in 'B'
    }
}


Myślę, że jest to bardzo przydatne. Package-private jest niezgrabne i wydaje się nieco przestarzałe. Ochrona przed dostępem z tego samego pakietu przypomina dziurę w hermetyzacji. Cieszę się, że Kotlin ma bardziej rygorystyczne podejście do działania słowa kluczowego protected.

A co, gdybyśmy chcieli coś podobnego do package-private w Kotlinie? W takim przypadku możemy rozważyć umieszczenie dwóch klas w tym samym pliku z oznaczeniem klas, które chcielibyśmy ukryć jako prywatne. Wszystkie metody mogą pozostać publiczne, ponieważ klasa i tak nie będzie dostępna z zewnątrz:

class C : B() {

    fun fooC() {
        val d = D()
        d.fooD()
    }
}

private class D {

    fun fooD() {
        println("fooD called")
    }
}


Więcej o modyfikatorach dostępu tutaj.

Podsumowanie

To by było na tyle. Mam nadzieję, że Ci się to przyda. Kotlin to świetny język i ma wiele fajnych funkcji. Polecam się dalej rozglądać i szukać innych fajnych funkcji.

Przyjemnego kodowania!
Vasya Drobushkov


Oryginał tekstu w języku angielskim możesz przeczytać tutaj

<p>Loading...</p>