Wiele razy sprawdzam, czy sprawdzam wartość zerową podczas pobierania wartości z jakiejś hierarchii danych, aby uniknąć wyjątków NullPointerExcept, które uważam za podatne na błędy i wymagają dużo dodatkowej analizy.
Napisałem bardzo prostą procedurę, która pozwala mi pominąć sprawdzanie wartości zerowej podczas pobierania obiektu ...
public final class NoNPE {
public static <T> T get(NoNPEInterface<T> in) {
try {
return in.get();
} catch (NullPointerException e) {
return null;
}
}
public interface NoNPEInterface<T> {
T get();
}
}
Używam tego trochę tak ...
Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());
Powyższe skutkuje otrzymaniem obiektu pokoju lub wartości zerowej, bez konieczności sprawdzania wszystkich poziomów nadrzędnych.
Co sądzisz o powyższym? Czy tworzę problematyczny wzór? Czy Twoim zdaniem jest na to lepszy sposób?
java.util.Optional
zamiast wartości zerowych w celu reprezentowania brakujących danych? Zapewnia to przydatne narzędzia zarówno dla opisywanego przypadku, jak i przypadków, w których chciałbyś nadal korzystać z danych domyślnych, a nie tylko zwracać stan awarii na końcu łańcucha.Option
(lubMaybe
) monadę :)Odpowiedzi:
Twoje rozwiązanie jest bardzo inteligentne. Problem, który widzę, polega na tym, że nie wiesz, dlaczego masz
null
? Czy to dlatego, że dom nie miał pokoi? Czy to dlatego, że miasto nie ma domów? Czy to dlatego, że kraj nie miał miast? Czy to dlatego, że byłnull
kolekcja w pozycji 0 z powodu błędu, nawet jeśli domy znajdują się w pozycjach 1 i wyższych?Jeśli użyjesz
NonPE
klasy extensibe , będziesz mieć poważne problemy z debugowaniem. Myślę, że lepiej wiedzieć, gdzie dokładnie łańcuch jest zerwany, niż po cichu zdobyćnull
co może kryć głębszy błąd.Narusza to również prawo Demeter :
country.getTown().getHouses().get(0).getLivingRoom()
. Częściej niż nie, naruszenie jakiejś dobrej zasady powoduje, że musisz wdrożyć niekonwencjonalne rozwiązania, aby rozwiązać problem spowodowany naruszeniem tej zasady.Radzę , abyś używał go ostrożnie i starał się rozwiązać usterkę projektową, która powoduje, że musisz ponieść atak na wrak pociągu (abyś nie musiał używać
NonPE
wszędzie). W przeciwnym razie możesz mieć błędy, które będą trudne do wykrycia.źródło
Option
monady nie ma znaczenia, gdzie w łańcuchu znajduje się nieobecna wartość. Kiedy dbasz o to, prawdopodobnie użyłbyś innego typu, npEither
.?.
i?[]
operatorów. Jednym z przykładów, kiedy możesz chcieć użyć takiej rzeczy, są hierarchiczne ustawienia po stronie serwera.var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;
Kogo obchodzi, dlaczego jakakolwiek jego część była zerowa? Może nie można pobrać ustawień. Być może zdecydowałeś się usunąć część ustawień. W każdym razie nigdy tak naprawdę nie możesz liczyć na uzyskanie ustawień serwera, więc ustawienie domyślne jest zwykle dobrym pomysłem i nie jest prawdopodobne, że przejmujesz się, dlaczego nie udało ci się uzyskać rzeczywistych ustawień, chyba że zdarza się to bardzo często, gdy nie powinno .settings.a.b.c
. Z drugiej strony jest to pojedynczy izolowany przykład.Pomysł jest w porządku, naprawdę dobry. Ponieważ
Optional
typy Java 8 istnieją, szczegółowe objaśnienie znajduje się w części Opcjonalne Java . Przykładem tego, co opublikowałeś, jestI dalej.
źródło
Optional
jest to bardziej czytelne rozwiązanie, choćby dlatego, że - w przeciwieństwie do twojej propozycji - jest to bardzo powszechny idiom. Jest jeszcze bardziej zwięzły niż twój!Twoja metoda działa wystarczająco dobrze, zgodnie z jej przeznaczeniem, choć zwraca
null
s, gdyNullPointerException
brzmi jak zły projekt.Staraj się unikać
null
s, kiedy możesz, i mijaj je tylko wtedy, gdy coś reprezentują lub mają specjalne znaczenie i zwracaj je tylko wtedy, gdy coś reprezentują / znaczą coś - w przeciwnym razie powinieneś rzucićNullPointerException
. Pozwala to uniknąć błędów i zamieszania. JeśliObject
nie powinno byćnull
,NullPointer
należy rzucić. Jeśli obiekt może być,null
wtedy nic nie pójdzie źle po przekazaniu go. W przeciwnym razie powyższa metoda działa.źródło
Czuję twój ból, ale proponowane rozwiązanie to zły pomysł.
NoNPE.get
.Optional.map
właśnie tego szukasz.Na marginesie,
NoNPEInterface
jest duplikatemjava.util.function.Supplier
.W niektórych przypadkach możesz rozważyć użycie narzędzi do oceny wyrażeń, które są obecne w wielu ramach (na przykład: EL, SpEL):
źródło