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:
private
jest dostępne w wnętrza klasypackage-private
jest dostępne, jak w private plusprotected
jest dostępne jako package-private plus dla wszystkich klas podrzędnychpublic
jest 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
.
public
jest dostępne z każdego miejscaprotected
jest dostępne tylko z klasy podrzędnejinternal
jest dostępne z całego modułu (nie tylko z pakietu, jest to „publiczne w module”)private
jest 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.