Rzucanie nulli w Kotlinie

0

W Javie żeby nie rzucać nullami i łatwiej je przetwarzać powstała klasa Optional. W Kotlinie takiej klasy nie ma i by default wszystko jest null safety. Czasami jednak zdarza się że chcemy pobrać jakąś wartość (np. z bazy) której nie ma. Oczywiście możemy rzucać Either i inne tego typu pokroju obiekty ale pytanie czy rzucanie nullem w Kotlinie ma sens? Jakby tak podejść do tematu generycznie to pewnie jest to bezsensu bo null to nadal null ale z drugiej strony w kotlinie przetwarzanie nulli w stylu np.:

    value?.let { doSomething } 

jest poniekąd podobne do tego co robimy z Optionalem w Javie (np. map).

Pytanie więc. Rzucanie nulli w Kotlinie jest akceptowalne czy raczej bee?

0

Ja bym powiedział, że dopóki nie używasz non-null assertion w kodzie (!!) i nie masz wielokrotnie zagnieżdżonych let albo innych dziwnych baboli, to jest ok. Jak zaczyna się robić gmatwanina z powodu obsługi nulli to wtedy nie ma wyjścia, refaktor żeby to uprościć.

W sumie najgorzej jest, gdy jakaś biblioteka Javowa pakuje nulle do typu non-null przez refleksje. Wtedy toto już nawet NPE nie rzuca.

1

Kotlinowe ? to po prostu odpowiednik Optional z javy, tylko lepsze. Poza tym zobacz ze tu sytuacja jest zupełnie odwrotna niz w javie. W javie jesli widzisz ze metoda zwraca Object to spodziewasz się dostać Object a czasem niespodziewanie dostaniesz nulla. Jesli masz Optional to wiesz ze mozesz miec "null". W kotlinie domyślnie nie mozesz miec null więc na pewno dostaniesz Object. Jeśli w sygnaturze masz Object? no to z góry wiesz, że musisz to obsłużyć (kompilator Ci chyba nawet nie pozwoli inaczej).

Więc podsumowując: używanie null w kotlinie to tak na prawdę dziania podobne jak używanie Optonal. No chyba, że każda metoda zwraca ? no ale to juz lekka patologia

0

Niestety nie rozumiem stwierdzenia "Rzucanie nulli". Czy masz na myśli rzucanie wyjątku NullPointerException czy przepychanie nulla jako wartość?
Według paradygmatu programowania fukcyjnego rzucanie wyjątków jest złe. Więc jeśli chcesz przepychać nulla możesz albo użyć notacji ze znakiem zapytania (jak w twoim przykłądzie) albo użyć bibliotekę Arrow-kt zawierającą klasę Option.
Dobrze użyta biblioteka Arrow-kt robi z Kotlina funkcyjny język podobny do Haskella. Rezultat jest podobny do użycia bibliotek ScalaZ/Cats dla Scali.

3

Wg mnie, wykorzystywanie null w jakiejkolwiek logice powinno być uznawane za bad design.

Jeśli Ci się chce, zerknij sobie tu (lub znajdź sobie jakieś streszczenie tej prezentacji) - Tony Hoare - Null References: The Billion Dollar Mistake.

0

@catom: tylko jak w kotlinie masz metodę która zwraca Object? i zwrócisz tam null to jest to odpowiednik metody javowej w która zwraca Optional<Object> i zwracasz Optional.empty() Po prostu inna składnia

2
danek napisał(a):

Kotlinowe ? to po prostu odpowiednik Optional z javy, tylko lepsze.

Nie jestem tego 100% pewny. Rozpatrywane w izolacji T? jest ładniejsze od Optional<T>.
Ale fakt, że mamy jeszcze Either<E,T>, Try<T>, Future<T> i tony innych M<T> powoduje pytanie czy nie lepiej by było gdyby była składnia do obsługi dowolnych M<T> (poodstawa to tzw. do notation ).
Możliwe, że będą sobie pluć w brodę za jakiś czas.

Z drugiej strony nie jestem też pewny, że wybrali złe rozwiązanie. Po prostu widzę problem, ale nie wiem jak z tego wybrnąć dobrze i praktycznie. Ich rozwiązanie jakoś działa, ale poza obsługą nulli z javy i framorków nie stosuję T? tylko Option<T>. Co więcej nie mam przekonania, że dobrze robię...
(ale drażnią mnie trochę takie wyjątki w języku).

0

Odpowiem z perspektywny Androida, ale wydaje mi się, że pasuje to do wszystkich frameworków. Rzeczy typu !! albo requireNotNull() powinny być używane tylko wtedy, kiedy framework za nas coś tworzy, przekazujemy potem tam jakiś parametr i w pewnym momencie chcemy go użyć, a jego brak oznaczałby błąd programu. Wtedy !! wywala nam apkę i od razu pokazuje, gdzie mamy błąd, który musimy naprawić. Czasami może być to błąd frameworka, więc trzeba go zgłosić i tymczasowo korzystać z jakiegoś obejścia do czasu naprawy. W normalnym kodzie uważam, że nigdy nie powinno wystąpić !! czy requireNotNull().

catom napisał(a):

Wg mnie, wykorzystywanie null w jakiejkolwiek logice powinno być uznawane za bad design.

Jeśli Ci się chce, zerknij sobie tu (lub znajdź sobie jakieś streszczenie tej prezentacji) - Tony Hoare - Null References: The Billion Dollar Mistake.

Nie zgadzam się z tym popularnym stwierdzeniem. null sam w sobie nie jest problemem. Problemem jest niemożność rozpoznania nulla na etapie kompilacji przez większość języków. Jeśli mamy brak wartości, to śmiało możemy używać nulli, jeśli pozwala nam na to system typów i ma to sens z perspektywy modelowania.

jarekr000000 napisał(a):

Z drugiej strony nie jestem też pewny, że wybrali złe rozwiązanie. Po prostu widzę problem, ale nie wiem jak z tego wybrnąć dobrze i praktycznie. Ich rozwiązanie jakoś działa, ale poza obsługą nulli z javy i framorków nie stosuję T? tylko Option<T>. Co więcej nie mam przekonania, że dobrze robię...
(ale drażnią mnie trochę takie wyjątki w języku).

Ja mam w drugą stronę. Stosuję T? a wydaje mi się, że bardziej eleganckie byłoby Option<T>. Na Androidzie mogę sobie jeszcze to tłumaczyć tym, że khem, kehm wydajność, ale to trochę oszukiwanie samego siebie. Option<T> ma natomiast ogromną przewagę w przypadku korzystania z Reactive Streams. Specyfikacja tego po prostu zabrania i choćbym nie wiadomo jak tego chciał, to muszę to opakować w jakiś kontener. Z drugiej strony teraz pojawiło się Flow w Kotlinie, więc pewnie będzie za jakiś czas de-facto standardem i problem zniknie.

1
Michał Sikora napisał(a):

Jeśli mamy brak wartości, to śmiało możemy używać nulli, jeśli pozwala nam na to system typów i ma to sens z perspektywy modelowania.

Jeśli mamy brak wartości, to zdecydowanie wolę to jawnie zakomunikować używając monady maybe / option. Oczywiście, nie czepiałbym się nulli w np. RDBMS, ale przy mapowaniu ich struktur na odpowiednie struktury języka obiektowego / funkcyjnego, zdecydowanie wolę, gdy wszystkie nullowalne wartości jawnie to w takim języku deklarują.

Aż mi się przypomniał jeden projekt, który miałem przyjemność utrzymywać, gdzie co 5 linii było wyrażenie typu if(service.calculateSomething(attribute) != null) {...} itd. Analiza takiego kodu naprawdę nie należy do najprzyjemniejszych.

0

Aż mi się przypomniał jeden projekt, który miałem przyjemność utrzymywać, gdzie co 5 linii było wyrażenie typu if(service.calculateSomething(attribute) != null) {...} itd. Analiza takiego kodu naprawdę nie należy do najprzyjemniejszych.

A czy był to język, który pozwalał na rozpoznawanie nulli w typie?

0

Właśnie kotlin po przez ? sygnalizuje możliwość nulla

0
jarekr000000 napisał(a):

Nie jestem tego 100% pewny. Rozpatrywane w izolacji T? jest ładniejsze od Optional<T>.
Ale fakt, że mamy jeszcze Either<E,T>, Try<T>, Future<T> i tony innych M<T> powoduje pytanie czy nie lepiej by było gdyby była składnia do obsługi dowolnych M<T> (poodstawa to tzw. do notation ).
Możliwe, że będą sobie pluć w brodę za jakiś czas.

Z drugiej strony nie jestem też pewny, że wybrali złe rozwiązanie. Po prostu widzę problem, ale nie wiem jak z tego wybrnąć dobrze i praktycznie. Ich rozwiązanie jakoś działa, ale poza obsługą nulli z javy i framorków nie stosuję T? tylko Option<T>. Co więcej nie mam przekonania, że dobrze robię...
(ale drażnią mnie trochę takie wyjątki w języku).

Nie znam za bardzo kotlina, ale zastanawia mnie co daje w tej sytuacji używanie języka który buduje tone abstrakcji nad null-safety po to tylko żeby babrać się dalej z klasami typu Option/Optional ? Skoro i tak jest jakiś tam impakt na wydajności (?) ? Im więcej słyszę o kotlinie tym bardziej zaczyna mi się wydawać ze to kolejna modna rzecz w IT, na którą jest chwilowe parcie na szkło, a za rok/dwa nikt juz o tym nie będzie pamiętać a java będzie miała się dobrze? Jak bardzo warto jest przenieść się do języka "nowoczesnego" po to żeby sprzedać javowe dobrze znane problemy na kotlinowe?

Podsumowując jak juz używamy Option to jedyny zysk z kotlina to gettery/settery i trochę zwięźlejsza składnia tak ? Hmm to nie wiem czy nie wole lomboka i javy, przynajmniej za pół roku nikt nie będzie musiał przepisywać tych aplikacji.

3
Interpod napisał(a):

Nie znam za bardzo kotlina, ale zastanawia mnie co daje w tej sytuacji używanie języka który buduje tone abstrakcji nad null-safety po to tylko żeby babrać się dalej z klasami typu Option/Optional ?

O jakiej tonie abstrakcji piszesz? Znak zapytania przy typie, to chyba najprostsze rozróżnienie jakie może być w przeciwieństwie do Option, który już jest jakąś abstrakcją.

Skoro i tak jest jakiś tam impakt na wydajności (?) ?

Nie ma. A jeśli komuś zależy na tych nanosekundach, to może wyłączyć podczas kompilacji, żeby nie były dodawane checki dla nulli w publicznych metodach.

https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-2-324a4a50b70

Jak bardzo warto jest przenieść się do języka "nowoczesnego" po to żeby sprzedać javowe dobrze znane problemy na kotlinowe?

A jakie problemy przenosisz z Javy do Kotlina korzystając z Option?

Podsumowując jak juz używamy Option to jedyny zysk z kotlina to gettery/settery i trochę zwięźlejsza składnia tak? Hmm to nie wiem czy nie wole lomboka i javy, przynajmniej za pół roku nikt nie będzie musiał przepisywać tych aplikacji.

Zależy, co masz na myśli. Bo pod "trochę zwięźlejszą składnią" może kryć się wszystko.

  • delegacje
  • val, var
  • inferencja typów
  • data klasy
  • destrukcje
  • sealead klasy
  • internal
  • korutyny
  • wbudowana składnia dla funkcji
  • funkcje i właściwości rozszerzające
  • typealias (to trochę bieda, ale patrz punkt niżej)
  • inline klasy
  • wsparcie dla wielu platform
  • wyrażenia zamiast intrukcji
  • parę innych rzeczy do lukru składniowego

Lombok poza data class i val, var niczego z tej listy nie oferuje.

2
Interpod napisał(a):

Nie znam za bardzo kotlina, ale zastanawia mnie co daje w tej sytuacji używanie języka który buduje tone abstrakcji nad null-safety po to tylko żeby babrać się dalej z klasami typu Option/Optional ? Skoro i tak jest jakiś tam impakt na wydajności (?)

Impakt na wydajności jest - w microenchmarkach. Trzeba mieć pecha, żeby zobaczyć na produkcji. Tym niemniej, bezpieczeństwo kosztuje, trzeba wybrać czasem co ważniejsze.
Jak kiedyś wyjdzie mi, że w jakimś kluczowym miejscu Option mi spowalnia to go z radością tam przepiszę na null. Na razie nawe nie byłem blisko.
Będzie jeszcze mniejszy narzut Optiona jak wprowadzą ValueTypes (Valhalla).

Podsumowując jak juz używamy Option to jedyny zysk z kotlina to gettery/settery i trochę zwięźlejsza składnia tak ? Hmm to nie wiem czy nie wole lomboka i javy, przynajmniej za pół roku nikt nie będzie musiał przepisywać tych aplikacji.

Pomijając już całego lomboka, którego najważniejsze sensowne zastosowanie widzę w procesie pozbywania się jedzenia z żołądka. To jeśli rzeczywiście używasz go głównie do setterów i getterów to naprawdę współczuje tym co za rok - dwa nie będą mogli przepisać twoich aplikacji. W kotlinie chyba tych setterów to użyłem raz, bo z durnym frameworkiem pracowałem.

0
danek napisał(a):

Kotlinowe ? to po prostu odpowiednik Optional z javy, tylko lepsze.

Nie całkiem. Np. w RxJavie (2.x) nie mogę zastosować typu Observable<Something?>. Zgodnie ze specyfikacją Reactive Streams, strumienie nie przyjmują nulli w ogóle. Mogę więc być zmuszony do stworzenia jakiegoś własnego wrappera - zaimprowizowanego Optionala - jeśli nie jestem w stanie zlikwidować nullowalności (bo np. źródło danych, które czasem bywają nullami, leży poza moim kodem).

Inaczej jest w Swifcie, gdzie typy "nullowalne" są, tak naprawdę, słodzikiem składniowym na Optionale właśnie, a nie prawdziwymi nullami. Tyle, że twórcy Swifta nie musieli się martwić interoperowalnością z Javą.

1 użytkowników online, w tym zalogowanych: 0, gości: 1