Czego oczy nie widzą…
Oczywiście popieprzyły mi się tygodnie. Długi weekend jest w kolejnym, nie w tym. Jednak bez paniki. Mamy co robić. Wracamy do Kotlina. Dziś krótko o widoczności elementów.
Kotlin ma trochę inne podejście do spraw widoczności elementów.
Domyślnie – publiczne
Zasada działania domyślnej widoczności w Javie jest ogólnie znana. Motywacja takiego, a nie innego rozwiązania tego problemu już nie. Domyślną widocznością w Javie jest widoczność pakietowa. Ma to związek z założeniem, że Java ma być bezpieczna w użyciu. Inaczej mówiąc język zmusza do pisania kodu choć trochę idioto-odpornego. Jedną z cech takiego kodu jest komunikacja po interfejsach. Stąd domyślna widoczność w interfejsach to public. Wniosek nasuwa się sam – pakiet powinien wystawiać API oparte o interfejsy, a klasy powinny być widoczne tylko w pakiecie. Teoria, teorią, ale życie to nie je bajka i obecnie zakłada się, że najwygodniej jest, gdy domyślnie wszystko jest publiczne.
Tak też jest w Kotlinie. Domyślna widoczność to public:
Listing 1. Wszystko publiczne
package pl.koziolekweb.visibility
fun doSomething(){}
class IMSomething{
fun doSomething(){}
}
var question:String = "Universal question of life?";
val answer = 42;
Jak widać, możemy doprowadzić w ten sposób, do powstania zmiennych globalnych. Fajna mina, na którą łatwo wdepnąć.
Modyfikator protected działa co do zasady tak samo, jak w Javie. Nie można go zastosować do funkcji i zmiennych swobodnych. Nuuudaaa…
Prywatne jest prywatne
Przyjrzyjmy się kodowi napisanemu w Javie:
Listing 2. Czy metoda jest prywatna?
public class SomeClass {
public void doSomething(){
Inner inner = new Inner();
inner.innerMethod();
}
class Inner{
private void innerMethod(){}
}
}
Ten kod skompiluje się i będzie działać. Problem polega jednak na tym, że prywatność metody innerMethod jest „antyterrorystyczna”. Czyli formalnie jest, ale jednak można ją wywołać spoza obiektu tej klasy. Ten sam kod w Kotlinie:
Listing 3. To samo w Kotlinie
class IMSomething{
fun doSomething(){
val inn = Inner();
inn.innerFun()
}
class Inner{
private fun innerFun(){}
}
}
W tym przypadku kod nie skompiluje się, ponieważ innerFun jest niewidoczna. Ogólna zasada brzmi – elementy prywatne klasy zawsze są niewidoczne na zewnątrz klasy. Trochę inaczej brzmi ta reguła w odniesieniu do funkcji i zmiennych swobodnych.
Listing 4. Funkcje prywatne
private fun IMPrivate(){}
var dontSetMeNow = "You cannot set me"
private set
Funkcja IMPrivate oraz setter dla dontSetMeNow będą widoczne tylko w pliku, w którym zostały zdefiniowane.
Widoczność modułowa
To jest ten mały smaczek, który odróżnia Kotlina od Javy. Kod w większych projektach zazwyczaj zorganizowany jest w moduły. Mogą to być podprojekty mavena, gradle, moduły IntelliJ, albo też kompilacje anta (pliki kompilowane w ramach jednego wywołania kompilatora).
Jeżeli chcemy by nasza klasa albo funkcja były widoczne tylko w ramach modułu to używamy modyfikatora internal.
Listing 5. Widoczność modułowa
internal fun IMModuleFunction(){}
internal class OnlyInThisModule{
internal fun notForOtherModules(){}
}
internal val version=1.0
Zmienne swobodne też mogą być widoczne tylko w ramach modułu.
Widoczność taka jest kompromisem pomiędzy publiczną a pakietową. Ma dość duży potencjał. Szczególnie w projektach wielomodułowych, gdzie zależy nam na odseparowaniu część kodu od użytkownika, a jednocześnie chcemy go swobodnie wykorzystywać w naszym module.
Podsumowanie
Kotlin różni się od Javy w zakresie modyfikatorów dostępu. Niektóre różnice są niewielkie jak w przypadku private. Inne, jak internal, są nową jakością. Otwiera to nam nowe możliwości przy projektowaniu naszego kodu. Łatwość współpracy pomiędzy Kotlinem a Javą przyprawiona drobnostkami jak widoczność modułowa jest bardzo cenna. Naprawdę warto zastanowić się nad wdrożeniem Kotlina w swoim projekcie.