2015-07-19 9 views
17

W mojej obecnej pracy jesteśmy przepisanie kodu Java 8. Jeśli masz kodu:Jak przepisać kod na opcje?

if(getApi() != null && getApi().getUser() != null 
    && getApi().getUser().getCurrentTask() != null) 
{ 
    getApi().getUser().getCurrentTask().pause(); 
} 

można po prostu przepisać go do

Optional.ofNullable(this.getApi()) 
.map(Api::getUser) 
.map(User::getCurrentTask) 
.ifPresent(Task::pause); 

bez zmiany zachowań kodu. ale co, jeśli coś na środku może rzucić NPE, ponieważ nie jest zaznaczone na zero?

na przykład:

if(getApi() != null && getApi().getUser() != null 
    && getApi().hasTasks()) 
{ 
    getApi().getMasterUser(getApi().getUser()) //<- npe can be here 
    .getCurrentTask().pause(); 
} 

co jest najlepszym sposobem, aby przepisać kod takiego korzystania ewentualne, (powinien działać dokładnie tak samo i rzucać NPE kiedy getMasterUser(...) zwraca null)

UPD Drugi przykład:

if(getApi()!=null && getApi.getUser() != null) 
{ 
    if(getApi().getUser().getDepartment().getBoss() != null)// <- nre if department is null 
    { 
     getApi().getUser().getDepartment().getBoss().somefunc(); 
    } 
} 

ma wartości zerowe dla api, użytkownika, szefa, ale n z działu. jak to zrobić za pomocą opcji?

+0

Pierwszy wiersz kodu ma bitową wartość AND, czy to prawda? – Mike

+0

Oczywiście to był błąd @Mike – maxpovver

+0

Czy nie powinno być 'Opcjonalne.Niebłyski (this :: getApi)' zamiast 'Opcjonalnie.Niepliku (this.getApi())'? –

Odpowiedz

7
if(getApi() != null && getApi().getUser() != null) { 
    if(getApi().getUser().getDepartment().getBoss() != null) { 
     getApi().getUser().getDepartment().getBoss().somefunc(); 
    } 
} 

Jednym ze sposobów na piśmie, to z optionals jest:

Optional.ofNullable(this.getApi()) 
    .map(Api::getUser) 
    .map(user -> Objects.requireNonNull(user.getDepartment())) 
    .map(Department::getBoss) 
    .ifPresent(Boss::somefunc); 

Ale to jest podatne na błędy, ponieważ wymaga od klienta, aby śledzić, co jest i nie jest opcjonalne. Lepszym sposobem byłoby sprawić, by sam api zwrócił wartość opcjonalną zamiast wartości zerowej. Następnie kod klienta jest:

this.getApi() 
    .flatMap(Api::getUser) 
    .map(user -> user.getDepartment().getBoss()) 
    .ifPresent(Boss::somefunc)); 

spowodowałoby to wyraźniejsze w API, które wartości powinno być dobrowolne i sprawiają, że błąd czasu kompilacji nie je obsłużyć.

if(getApi() != null && getApi().getUser() != null && getApi().hasTasks()) { 
    getApi().getMasterUser(getApi().getUser()).getCurrentTask().pause(); 
} 

Tutaj potrzebny jest dostęp do api i user w tym samym czasie, więc prawdopodobnie trzeba gniazdem lambdy:

getApi().filter(Api::hasTasks).ifPresent(api -> { 
    api.getUser().ifPresent(user -> { 
     api.getMasterUser(user).getCurrentTask().ifPresent(Task::pause); 
    }); 
}); 
+0

Drugi wygląda całkiem blisko tego, czego potrzebuję, ale wymaga przepisania starszego kodu i całego kodu, który używa tych funkcji, co zwykle jest niemożliwe. A więc pojawia się napis '.mapStrict (map_func, error_func)' wywołujący 'error_func', jeśli' map_func' został wywołany i zwrócił wartość null? – maxpovver

+1

@maxpovver Myślę, że najbliższy jest ci: 'map (użytkownik -> Objects.requireNonNull (user.getDepartment()))'. – fgb

+0

dzięki. plz dodaj kod z komentarza do odpowiedzi, abym mógł go zaakceptować. Nadal nie jest doskonały, ponieważ monada powinna to sprawdzić, a nie funkcja stosowana do monady, ale jest lepsza niż nic. – maxpovver

2

Więc odpowiedź na pierwszym przykładzie jest

Optional.ofNullable(getApi()) 
.filter(Api::hasTasks) 
.map(Api::getUser) 
.map(u -> Objects.requireNonNull(getApi().getMasterUser(u)))//api won't be null here so no need to check it 
.map(MasterUser::getCurrentTask) 
.ifPresent(Task::pause); 

i dla drugiego przykładu:

Optional.ofNullable(getApi()) 
.map(Api::getUser) 
.map(u -> Objects.requireNonNull(u.getDepartment())) 
.map(Department::getBoss) 
.ifPresent(Boss::somefunc); 

Musisz zmienić .map(class::func) na .map(o -> Objects.requireNonNull(o.func())), aby rzutował NRE w razie potrzeby.

Łamie monada wzór oczywiście, ale wciąż jest lepsza niż brak jakiegokolwiek rozwiązania

Popraw mnie jeśli się mylę proszę.

3

Na drugim przykładzie (dotyczy pierwszej, jak również), jest krótsza i mniej więcej tak oczywiste, jak w dłuższej wersji:

Optional.ofNullable(getApi()) 
.map(Api::getUser) 
.flatMap(u -> Optional.ofNullable(u.getDepartment().getBoss())) 
.ifPresent(Boss::somefunc); 

opiera się również na mniej API.

Chciałbym dodatkowo skomentować twoje "to łamie monadę" — nic tutaj (łącznie z twoimi rozwiązaniami) nie łamie monadowego wzorca. Jest w pełni wyrazicielem pod względem return i >>=. Jeśli cokolwiek, jest to wywołanie ifPresent, które je zrywa, ponieważ implikuje skutki uboczne.