Optional - często go używacie?

2

Siema. Interesuje mnie, czy zawsze, w każdym możliwym przypadku NPE używacie Optionala? Czy jednak nie? Jeśli mamy linijkę np

object.getA().getB().getC();

to tych potencjalnych NPE jest sporo, ale użycie Optionala tutaj doda kolejny narzut kodu. Wiadomo, lepiej mieć kilka linijek więcej i mieć gwarancję że akurat NPE nam nie wy3.14eprzy działania programu.

Co też myślicie o następującej sytuacji - mamy sobie publiczną metodę X, która jako argument przyjmuje jakiś skomplikowany obiekt i wewnątrz tej metody jest taki łańcuch getów jak wyżej. Natomiast wiemy doskonale, że tej metody użyliśmy jedynie w 2 -3 miejscach i tam mamy pewność, że obiekt te wszystkie swoje pola i podpola ma. Czy fakt, że do danej metody ma się dostęp w obrębie całej aplikacji, powoduje konieczność "handlowania" potencjalnymi nullami (już pomińmy inne przypadki brzegowe)? Czy też przenosimy odpowiedzialność na użytkownika - wpakował nulla, to dostaje NPE na twarz i elo.

Zachęcam do wypowiadania się @Shalom @jarekr000000 @Koziołek @scibi92 @Krolik @katelx

5

Przykład który podałeś łamie Law of Demeter ;)

0

preferuje uzywanie null object pattern w wiekszosci miejsc gdzie optional moglby byc jakims rozwiazaniem.
btw, imo zwykle robienie czegos takiego jak object.getA().getB().getC() w metodzie swiadczy o innych zlych praktykach np. czemu nie przekazac do metody tego co zwraca getC() zamiast robic taki lancuch wywolan (poza uzywaniem fluent interface, no ale wtedy to raczej ciezko o NPE ;))

3

@Pinek odpowiadajac na pytanei z komentarza, chodzi mi o to ze lepiej wyglada powiedzmy:

class Country {
    private Age adultAge;
    boolean isAdult(Age age) {
        return age.value() >= adultAge.value();
    }
}

niz

boolean isAdult(StaticDataEntry data) {
    return data.getPerson().getAge().value() > data.getPerson().getCountryOfResidence().getAdultAge().value();
}

oczywiscie zamodelowanie aplikacji tak zeby mozliwe bylo uzycie isAdult z przykladu 1) wymaga wiecej wysiku ale zwraca sie wlasnie przy obsludze bledow

2

@Pinek nigdy nie masz pewności że ktoś tam kiedyś da nulla, a jeśli tak postanowi to powinien wtedy automatycznie zmienić sugnaturę metody na Optional i obsłużyć w kodzie przypadek kiedy nie ma wartości. Nie należy wszystkiego opakować Optionalem, bo to bez sensu. Należy dać Optional wtedy kiedy widzimy że możemy / chcemy zwracać "pustą" wartość.
Poza taka wiązanka get.get.get to tragedia i klasyczne przeciekające warstwy/gwałt na enkapsulacji. Jeśli coś takiego występuje to rób delegacje tak żeby finalnie mieć tylko x.get() a reszta ukryta pod spodem.

1

Tutaj nie NPE jest problemem tylko to że nie masz informacji co może być nullem a co nie.

W podejściu:

object.getA().getB().getC();

rozumiem że każdy z tych elementów to po prostu uniwersalny getter wykorzystywany różnie w wielu miejscach i kontekstach.

W podejściu:

object.getCOfBInA(); 

masz w miarę dokładny cel wywołania A i B, w związku z tym możesz określić czy chcesz w tej sytuacji się wywalić "NoDataFound!" czy zwrócić po prostu null (i opisać to w JavaDoc lub zastosować Optional).

0

@vpiotr:

Okej, wszystko co napisałeś jest fajne i zgodne ze sztuką. Ale w praktyce siadasz do projektu w pracy i np masz model z 15 polami, więc już samych getterów/setterów jest 30. A te pola to nie zwykłe prymitywy, tylko kolejne obiekty modelowe. Dołożenie tylu kombinacji spowodowałoby 70-100 metod getterowych w klasie modelu :( I cóż wtedy zaradzić?

0

@Pinek nowy kod pisac juz dobrze, stary powoli zmieniac przy okazji :)

7

Pomijając uwagi jak powyżej to piszem tak:

object.getA().flatMap(a -> a.getB()).flatMap(b ->b.getC()).forEach( c ->  zrobCosTam(c));

i jakoś idzie z tymi optionalami.

A co do nulli - ja tam lubie ludzi częstować NPE jeśli zasłużyli. Dajesz nulla do moje metody to witaj się z NPE.
Jakbym napisał, że przyjmuje nulla to móglbyś dawać (ale przecież bym nie napisał tylko powiedzieł, że argument jest Optional).
Generalnie wszelkiego rodzaju asserty, predykaty sprawdzające czy coś jest nullem akceptuje, czasem robię.
Fajne jest np. Objects.requireNonNull()
Ale najczęściej nie wrzucam nawet takich asercji . (Zależy w jakim kodzie). Bo to z pewnością ułatwia znalezienie błędu, ale z drugiej strony jest to szum informacyjny - potem metoda ma 2 linijki sensownego kodu plus dodatkowe 5 z assertami. Nie warto wszędzie śmiecić.

Wszelkiego rodzaju if( x != null) w logice to już zalążek zła.
No a najgorsze zło, które często spotykam w kodzie różnych firm i "konsultantów" to to:

public T method1(Type arg1)  {
   if ( arg1 != null ) { 
   .... bla bla bla that's just code
  } else {
   return null;
  }
}

Powinien być specjalny krąg piekiełka programistycznego dla tych co tak piszą.

9

U mnie w firmie był szał na Optionale i niestety wyglądało to tak, że były wypchane wszędzie. Na przykład można było się spotkać z takim czymś:

jakisOptional.orElse(null)

Generalnie trzeba używać tego z głową, ale to chyba jak wszystkiego :)

7

Piękne

5

object.getA().getB().getC();

The Getter Train has no breaks

77342405.jpg


Optional.nullable(myObject).map(myObject::getA).map(A::getB).map(B::getC);

I teraz pozostaje problem wartości w orElse. Można spróbować z null, ale tylko wtedy, gdy wypluwasz wszystko do GUI i masz tam jakieś regułki obsługi null/pusty String. W przeciwnym wypadku kulturalnie jakiś błąd jeżeli nie masz wartości domyślnej :) W ostateczności można zignorować, ale wtedy warto mieć uzasadnienie.

1
Pinek napisał(a):

Okej, wszystko co napisałeś jest fajne i zgodne ze sztuką. Ale w praktyce siadasz do projektu w pracy i np masz model z 15 polami, więc już samych getterów/setterów jest 30. A te pola to nie zwykłe prymitywy, tylko kolejne obiekty modelowe. Dołożenie tylu kombinacji spowodowałoby 70-100 metod getterowych w klasie modelu :( I cóż wtedy zaradzić?

To co opisałeś to nie obiekty tylko struktury danych (czyli wszystkie/większość pól dostępna na zewnątrz).
Osobiście nie mam nic przeciwko programowaniu strukturalnemu, ale trzeba mieć świadomość że się je uprawia.
I wtedy można zrobić coś takiego:

public class MyTest {
  public ClassC getC() {
     ClassA objA = getA();
     return getC(objA);
  }

  private ClassA getA() {
     return new ClassA(); // zrodlo dowolne
  }

  private ClassC getC(ClassA obj) {
     return obj != null ? obj.getB() != null ? obj.getB().getC() : null : null; // wyrazenie dowolnie
  }
}

// i na koncu
object.getC(); 

I teraz najważniejsze: ten kod powyżej jest rozwlekły i wydaje się nadmiarowy - i taki ma być.

Bo masz takie ścieżki wyboru:
a) albo Twój (kolejkowy) kod dowolnie permutuje sobie wywołania struktur danych w programie i nie panujesz nad tym (faktura wyciąga sobie dane o płci ze struktury HR-owej z CV-ki)
b) albo wiesz co robisz i jesteś pewien że możesz zrobić właśnie tak getC() i będzie to rozwiązanie stabilne.
c) albo robisz CQRS / fasadę które na potrzeby zebrania potrzebnych danych odpalają odpowiednie zapytanie które mieli po bazie

Rozwiązanie (a) wydaje się najwygodniejsze, ale oznacza brak architektury przepływu danych.
Rozwiązanie (b) ukrywa ale i usztywnia dostęp do danych. Zwiększa pracochłonność rozwiązania. Stosowane głównie w starszych architekturach.
Rozwiązanie (c) jest płynne - gdy zmienia się źródło danych zmieniasz zapytanie i po sprawie. Zapytanie łatwo przekierujesz na inną tabelę czy powiązanie. Jeśli wynik (Proxy, Mapa, DTO itp) się nie zmienia to klienci klasy nie muszą być przebudowywani.

A Optionala na razie nie używam - w pracy Java 7. Pewnie do jakichś prostych wywołań, jednostopniowych bym użył. Ale nie do takiej kolejki.

1

Opcjonal jest wtedy dobry jak wiemy że jakiś parametr jest ... opcjonalny czy to metody czy zwracany. Wedlug mnie na przykład może być ok jeśli zwracamy mamy DAO i zwracamy wynik wyszukiwania po unikatowej kolumnie, np.

   Optional<Book> findBookByISBN(String ISBN);

1

optionali uzywam jak wiem ze jakies pole lub wynik metody jest opcjonalny + ten optional bedzie "użyteczny" tzn za chwile nie zostanie zamieniony na nulla "bo trzeba"

1

W ogóle nie rozumiem jaka jest korzyść z tego Optional. Jak dla mnie to utrudnianie sobie życia, sztuka dla sztuki.

No bo powiedzcie mi w czym taki kod:

import java.util.Optional;

public class adhoc {
	public static void main(String[] args) {

		Integer x = null;
		Optional optionalX = Optional.ofNullable(x);
		int xx = (int) optionalX.orElse(0);
		
		System.out.println(2 * xx);
	}
}

jest lepszy od takiego:

public class adhoc {
	public static void main(String[] args) {

		Integer x = null;
		int xx = x == null ? 0 : x;
		System.out.println(2 * xx);
	}
}

W R też we wszystkich pakietach widze, że piszą na początku funkcji sprawdzania w stylu

if(is.na(x){
  x <- 0
}
2

@Pijany Kot w takim kodzie w ogóle nie ma to sensu bo masz tylko 1 poziom. Ale jak pobierasz wartość która przylatuje z jakiegoś obiektu 10 poziomów wyżej, jeszcze poprzez kilka hierarchii klas to wcale nie tak łatwo stwierdzić czy to może być null czy też nie. Możesz oczywiście cały kod opakować null checkami, ale to szaleństwo i sprawdzi się co najwyżej w g**no-skrypcie w R na kilka linijek. W projekcie na setki tysięcy linii to możesz o tym zapomnieć. Optional automatycznie wymusza obsłużenie wartości "pustej" i nie da sie tego nijak przeoczyć.

1

@Pijany Kot, widzisz nie rozumiesz co robisz w Javie i przykład nie za bardzo ci wchodzi.

public class adhoc {
    public static void main(String[] args) {
 
        Integer x = null;
        Optional optionalX = Optional.ofNullable(x);
        optionalX.ifPresentOrElse(
		xx-> System.out.println(2 * (int)xx),
		() -> System.out.println(0)
	);
    }
}

i nie trzeba nic kombinować z jakimś Ifem, z jakimś orElse czy przepisywaniem zmiennych. Optional zamyka ci wartość w kontenerze i daje możliwość manipulowania tą wartością z uwzględnieniem potencjalnego nulla.

2

Tak dodatkowo zarzucę tutaj tematem ze stacka gdzie Brian Goetz mówi dla jakich przypadków stworzyli optionale: https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555

1

TL;DR / i stary ten wątek chyba trochę?
Nie wiem czy było, ale chyba powinno się zacząć od linku ze SO:
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

0
podroznik napisał(a):

U mnie w firmie był szał na Optionale i niestety wyglądało to tak, że były wypchane wszędzie. Na przykład można było się spotkać z takim czymś:

jakisOptional.orElse(null)

Generalnie trzeba używać tego z głową, ale to chyba jak wszystkiego :)

W piątek przerabiałem taki kod. W Scali XD

1

Co to za odkop?

a wiesz że ten wątek jest z 2017?

Tak, przeglądałem sobie stare wątki i odkryłem że problem dalej występuje. A dokładniej ja muszę się z nim mierzyć w legacy. 10 linii kodu (dwa razy użyte orNull, raz case null => w switchu) zamieniłem na jedną linie _.flatMap(_._2).contains("y")

1

Javowski Optional byłby dużo piękniejszy, gdyby w jakiejś wersji kompilator w każdej metodzie Optional wymuszał not null (tzn "jakiegoś" optionala musisz zwrócić).

BTW koduję w głowie klasy/interfejsy Javowskie które są "hardwarowo" wbudowane w kompilator, ciągle jest zamiar podzielić się tym w jakiejś dyskusji bo mnie to zaciekawiło, ale odkłada się w czasie ...

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.