IllegalArgumentException czy NullPointerException dla parametru zerowego? [Zamknięte]

547

Mam prostą metodę ustawiania właściwości i nullnie jest ona odpowiednia dla tej konkretnej właściwości. Zawsze byłem rozdarty w tej sytuacji: mam rzucać IllegalArgumentException, albo NullPointerException? Z javadocs oba wydają się odpowiednie. Czy istnieje jakiś zrozumiały standard? Czy to tylko jedna z tych rzeczy, które powinieneś robić, co wolisz, i obie są naprawdę poprawne?

Mike Stone
źródło

Odpowiedzi:

299

Wygląda na IllegalArgumentExceptionto, że wywoływana jest funkcja an, jeśli nie chcesz nullbyć dozwoloną wartością, i NullPointerExceptionzostałaby ona wyrzucona, jeśli próbujesz użyć zmiennej, która się okazuje null.

Greg Hurlman
źródło
33
Poniższe odpowiedzi na to pytanie stanowią przekonujące argumenty, że wyjątek NullPointerException jest poprawnym wyjątkiem: stackoverflow.com/a/8160/372926 ; stackoverflow.com/a/8196334/372926 ; i stackoverflow.com/a/6358/372926 .
SamStephens
2
IllegalArgumentExceptionjest w sprzeczności z obiektami Java.requireNonNull (T) i Guava's Preconditions.checkNotNull (T), który rzuca a NullPointerException. Jednak właściwa odpowiedź jest zdecydowanie IllegalArgumentException wyjaśniona w doskonałej odpowiedzi Jasona Cohena i jej sekcji komentarzy .
matoni
417

Powinieneś używać IllegalArgumentException(IAE), a nie NullPointerException(NPE) z następujących powodów:

Po pierwsze, NPE JavaDoc wyraźnie wymienia przypadki, w których NPE jest odpowiedni. Zauważ, że wszystkie z nich są wyrzucane przez środowisko wykonawcze, gdy nullsą używane niewłaściwie. W przeciwieństwie do tego, JavaDoc IAE nie może być bardziej jasne: „Zgłoszony w celu wskazania, że ​​metoda została przekazana nielegalny lub nieodpowiedni argument”. Tak, to ty!

Po drugie, kiedy widzisz NPE w śladzie stosu, co zakładasz? Prawdopodobnie ktoś wyłuskał a null. Gdy widzisz IAE, zakładasz, że obiekt wywołujący metodę na górze stosu został przekazany w niedozwolonej wartości. Ponownie, to drugie założenie jest prawdziwe, pierwsze wprowadza w błąd.

Po trzecie, ponieważ IAE jest wyraźnie zaprojektowane do sprawdzania poprawności parametrów, musisz założyć, że jest to domyślny wybór wyjątku, więc dlaczego miałbyś zamiast tego wybrać NPE? Z pewnością nie dla różnych zachowań - czy naprawdę spodziewasz się, że kod wywołujący będzie przechwytywał NPE oddzielnie od IAE i w rezultacie zrobi coś innego? Czy próbujesz przekazać bardziej szczegółowy komunikat o błędzie? Ale i tak możesz to zrobić w tekście komunikatu wyjątku, tak jak w przypadku wszystkich innych niepoprawnych parametrów.

Po czwarte, wszystkie inne niepoprawne dane parametrów będą IAE, więc dlaczego nie być spójne? Dlaczego nielegalne nulljest tak wyjątkowe, że zasługuje na osobny wyjątek od wszystkich innych rodzajów nielegalnych argumentów?

Wreszcie, akceptuję argument podany przez inne odpowiedzi, że części interfejsu API Java używają NPE w ten sposób. Jednak interfejs API języka Java jest niespójny ze wszystkim - od typów wyjątków po konwencje nazewnictwa, więc myślę, że po prostu ślepe kopiowanie (ulubiona część) interfejsu API języka Java nie jest wystarczającym argumentem, aby podważyć te inne względy.

Jason Cohen
źródło
119
Skuteczne wydanie 2 w języku Java, pozycja 60: „Prawdopodobnie wszystkie błędne wywołania metod sprowadzają się do niedozwolonego argumentu lub stanu niezgodnego z prawem, ale inne wyjątki są standardowo stosowane w przypadku niektórych rodzajów nielegalnych argumentów i stanów. Jeśli wywołujący przekazuje wartość null w parametrze, dla którego wartości null są zabronione, konwencja nakazuje zgłaszanie wyjątku NullPointerException zamiast IllegalArgumentException. Podobnie, jeśli osoba wywołująca przekaże wartość spoza zakresu w parametrze reprezentującym indeks do sekwencji, wyjątek IndexOutOfBoundsException powinien zostać zgłoszony zamiast IllegalArgumentException. ”
Gili
33
JavaDoc dla NPE stwierdza również: „Aplikacje powinny zgłaszać instancje tej klasy, aby wskazać inne nielegalne użycie obiektu zerowego”. To może być bardziej jasne :(
R. Martinho Fernandes
12
Niestety, metody sprawdzania poprawności Validate.notNull(commons lang) i Preconditions.checkNotNull(guava) rzucają NPE :-(
Aaron Digulla
2
Chociaż Guava ma również Preconditions.checkArgument () zgłasza IllegalArgumentException ...
michaelok
16
@AaronDigulla, z dokumentów Guava: „Zdajemy sobie sprawę, że istnieje wiele ważnych argumentów przemawiających za rzuceniem IAE na argument zerowy. W rzeczywistości, gdybyśmy mieli maszynę czasu do cofnięcia się o> 15 lat, moglibyśmy nawet spróbować jednak postanowiliśmy pozostać przy preferencjach JDK i Effective Java NullPointerException. Jeśli jesteś niezachwiany w przekonaniu, że IAE ma rację, nadal masz checkArgument(arg != null), bez wygody zwracania arg, lub możesz utworzyć lokalne narzędzie do twojego projektu. ” code.google.com/p/guava-libraries/wiki/IdeaGraveyard
Arto Bendiken
162

Standardem jest rzucanie NullPointerException. Ogólnie nieomylna „Skuteczna Java” omawia to krótko w punkcie 42 (pierwsze wydanie), punkcie 60 (drugie wydanie) lub w punkcie 72 (trzecie wydanie) „Sprzyjanie stosowaniu standardowych wyjątków”:

„Prawdopodobnie wszystkie błędne wywołania metod sprowadzają się do niezgodnego z prawem argumentu lub stanu niezgodnego z prawem, ale inne wyjątki są standardowo stosowane w przypadku niektórych rodzajów nielegalnych argumentów i stanów. Jeśli osoba wywołująca przekaże wartość null w parametrze, dla którego wartości zerowe są zabronione, konwencja nakazuje, aby Zgłaszany jest wyjątek NullPointerException zamiast IllegalArgumentException. ”

GaryF
źródło
95
Definitywnie się z tym nie zgadzam. Wyjątki NullPointerException powinny być zgłaszane tylko wtedy, gdy JVM przypadkowo podąży za odwołaniem zerowym. To jest różnica, która pomaga, gdy jesteś wezwany, aby spojrzeć na kod o 3 nad ranem.
Thorbjørn Ravn Andersen
26
Niekoniecznie zgadzam się ze standardem (właściwie mógłbym pójść w obu kierunkach w tej kwestii), ale to właśnie takie jest standardowe użycie w JDK, stąd Efektywna Java przemawia za tym. Myślę, że jest to przypadek wyboru, czy przestrzegać standardu, czy zrobić to, co uważasz za słuszne. Jeśli nie masz bardzo dobrego powodu (a to z pewnością może się kwalifikować), najlepiej postępować zgodnie ze standardową praktyką.
GaryF
21
Ślad wyjątku pokazuje punkt wyjątku, więc jeśli różnica w rodzaju wyjątku powoduje dla ciebie piekło lub jest „różnicą, która ci pomaga”, robisz coś bardzo złego.
Jim Balter,
8
Fantazje i sofistyka. Tuż poniżej MB pisze: „Zauważ, że argumenty dotyczące twardego debugowania są fałszywe, ponieważ możesz oczywiście przekazać komunikat do NullPointerException, mówiąc, co było zerowe i dlaczego nie powinno być puste. Tak jak w przypadku IllegalArgumentException.”
Jim Balter
10
Jako oryginalny odpowiadający tutaj, powiem, że to nie ma aż tak wielkiego znaczenia. Z pewnością nie gwarantuje to 6 lat rozmowy. Wybierz jeden, który chcesz i zachowaj spójność. Standard, jak wskazałem, to NPE. Jeśli wolisz IAE z jakichkolwiek powodów, idź do niego. Po prostu bądź konsekwentny.
GaryF
138

Byłem zwolennikiem rzucania IllegalArgumentExceptionparametrów zerowych, aż do dzisiaj, kiedy zauważyłem java.util.Objects.requireNonNullmetodę w Javie 7. Za pomocą tej metody, zamiast robić:

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

możesz to zrobić:

Objects.requireNonNull(param);

i wyrzuci a, NullPointerExceptionjeśli przekazany parametr to null.

Biorąc pod uwagę, że ta metoda jest w samym środku, java.utiluważam, że jej istnienie jest dość silnym wskaźnikiem, że rzucanie NullPointerExceptionjest „Java sposobem robienia rzeczy”.

W każdym razie myślę, że tak.

Zauważ, że argumenty na temat twardego debugowania są fałszywe, ponieważ możesz oczywiście przekazać komunikat o NullPointerExceptiontym, co było puste i dlaczego nie powinno być puste. Tak jak z IllegalArgumentException.

Jedną dodatkową zaletą NullPointerExceptionjest to, że w kodzie o wysokiej wydajności można zrezygnować z jawnego sprawdzania wartości null (i NullPointerExceptionprzyjaznego komunikatu o błędzie) i polegać tylko na tym NullPointerException, że otrzymasz automatycznie, gdy wywołasz metodę na wartości null parametr. Pod warunkiem, że wywołasz metodę szybko (tj. Szybko się nie powiedzie), to masz zasadniczo ten sam efekt, ale nie tak przyjazny dla programisty. W większości przypadków lepiej jest to sprawdzić jawnie i rzucić użytecznym komunikatem wskazującym, który parametr ma wartość null, ale fajnie jest mieć opcję zmiany, jeśli wydajność decyduje bez zerwania opublikowanej umowy metody / konstruktora.

MB.
źródło
14
Guawa Preconditions.checkNotNull(arg)rzuca także NPE.
assylias
14
To tak naprawdę nie dodaje większej wagi do NullPointerException dla nielegalnych ARGUMENTÓW zerowych. Zarówno JDK wymaganyNonNull (), jak i Guava checkNotNull () mogą być wywoływane w dowolnym miejscu kodu, z dowolnym obiektem. Możesz na przykład wywołać je w pętli. wymagają, że nie można założyć, że jest wywoływane za pomocą niektórych argumentów metody i wymagają zgłaszania wyjątku IllegalArgumentException. Zauważ, że Guava ma również Preconditions.checkArgument (), który generuje IllegalArgumentException.
Bogdan Calmac
2
Trafny argument Bogdana, choć podejrzewam, że typowym (i ogólnie zamierzonym) zastosowaniem metody replaceNonNull jest sprawdzanie argumentów. Gdybyś musiał sprawdzić, czy coś nie ma wartości zerowej w pętli, pomyślałbym, że stwierdzenie byłoby typowym sposobem.
MB.
15
Myślę, że JavaDoc Object.requireNonNull () dodaje wagi do argumentu: „Ta metoda jest przeznaczona przede wszystkim do sprawdzania poprawności parametrów w metodach i konstruktorach”
Richard Zschech
Należy pamiętać, że argument wydajności na rzecz niejawnych kontroli zerowych jest często nieprawidłowy. JIT jest w stanie rozpoznać kontrole zerowe użytkownika i uchylić następującą domniemaną kontrolę zerową i / lub zastosować ten sam zestaw optymalizacji dla każdego typu kontroli zerowej. Zobacz tę wiki, aby uzyskać więcej informacji, w szczególności: Napisane przez użytkownika kontrole zerowe są w większości przypadków funkcjonalnie identyczne z tymi wstawionymi przez JVM. Oczywiście, jeśli robisz coś bardziej wymyślnego, np. Niestandardowe wiadomości, ta optymalizacja może nie mieć zastosowania.
BeeOnRope
70

Zwykle podążam za projektami bibliotek JDK, szczególnie kolekcji i współbieżności (Joshua Bloch, Doug Lea, ci faceci wiedzą, jak projektować solidne API). W każdym razie wiele interfejsów API w JDK aktywnie rzuca NullPointerException.

Na przykład Javadoc dla Map.containsKeystanów:

@ rzuca NullPointerException, jeśli klucz jest pusty, a ta mapa nie zezwala na klucze puste (opcjonalnie).

Wyrzucenie własnego NPE jest całkowicie poprawne. Konwencja polega na dołączeniu nazwy parametru, która była pusta w komunikacie wyjątku.

Wzór jest następujący:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

Cokolwiek zrobisz, nie pozwól, aby ustawiono złą wartość, i rzuć wyjątek później, gdy inny kod spróbuje go użyć. To sprawia, że ​​debugowanie to koszmar. Zawsze należy postępować zgodnie z zasadą „szybkiego działania”.

Mark Renouf
źródło
3
Myśl do przemyślenia: być może powodem, dla którego NullPointerException nie rozszerza IllegalArgumentException, jest to, że to pierwsze może wystąpić w przypadkach nieobejmujących argumentów metod.
Gili
2
@Gili: może ten problem występuje przede wszystkim, ponieważ Java nie obsługuje wielokrotnego dziedziczenia. Jeśli Java obsługuje MI, możesz wygenerować wyjątek, który dziedziczy po IllegalArgumentException i NullPointerException.
Lie Ryan
7
Komunikat nie musi zawierać argumentu, ponieważ zawsze byłby pusty, co daje: „null nie może być pusty”, niezbyt przydatne. :-) W przeciwnym razie, zgadzam się, możesz stworzyć „bogaty” NPE, z sensowną wiadomością.
PhiLho
5
Muszę się zgodzić - w razie wątpliwości postępuj zgodnie ze standardowym interfejsem API. Nie wszystko w interfejsie API jest optymalne, ale nadal jest utrzymywane i iterowane przez setki programistów i używane przez miliony programistów. Teraz w Javie 7 mamy inny przykład użycia NPE w ten sposób; metoda Objects.requireNonNull (T obj) - wyraźnie określona w celu sprawdzenia, czy odwołania do obiektów nie są puste, wyraźnie określona w celu sprawdzania poprawności parametrów w metodach / konstruktorach i wyraźnie określona, ​​aby wygenerować NPE, jeśli obiekt jest pusty. Koniec
flamming_python
44

Zagłosował za argumentem Jasona Cohena, ponieważ został dobrze zaprezentowany. Pozwól mi rozebrać to krok po kroku. ;-)

  • NPE JavaDoc wyraźnie mówi, „inne nielegalne zastosowania obiektu null” . Gdyby ograniczono się tylko do sytuacji, w których środowisko wykonawcze napotyka zero, kiedy nie powinno, wszystkie takie przypadki można by zdefiniować o wiele bardziej zwięźle.

  • Nic na to nie poradzę, jeśli założysz coś niewłaściwego, ale zakładając, że enkapsulacja jest stosowana właściwie, naprawdę nie powinieneś się przejmować ani zauważać, czy wartość zerowa została dereferencyjnie nieodpowiednia w porównaniu z tym, czy metoda wykryła nieodpowiednią wartość zerową i zwolniła wyjątek.

  • Chciałbym wybrać NPE nad IAE dla wielu powodów

    • Bardziej szczegółowo dotyczy charakteru nielegalnej operacji
    • Logika, która błędnie dopuszcza wartości zerowe, zwykle bardzo różni się od logiki, która błędnie dopuszcza wartości niedozwolone. Na przykład, jeśli sprawdzam poprawność danych wprowadzonych przez użytkownika, jeśli otrzymam niedopuszczalną wartość, źródłem tego błędu jest użytkownik końcowy aplikacji. Jeśli dostanę zero, to błąd programisty.
    • Nieprawidłowe wartości mogą powodować takie rzeczy, jak przepełnienie stosu, błędy braku pamięci, parsowanie wyjątków itp. Rzeczywiście, większość błędów ogólnie występuje w pewnym momencie jako niepoprawna wartość w niektórych wywołaniach metod. Z tego powodu postrzegam IAE jako NAJBARDZIEJ OGÓLNY ze wszystkich wyjątków w ramach RuntimeException.
  • W rzeczywistości inne nieprawidłowe argumenty mogą powodować różnego rodzaju wyjątki. UnknownHostException , FileNotFoundException , różne wyjątki dotyczące błędów składniowych, wyjątek IndexOutOfBoundsException , błędy uwierzytelnienia itp. Itp.

Ogólnie uważam, że NPE jest bardzo złośliwe, ponieważ tradycyjnie jest związane z kodem, który nie przestrzega zasady szybkiego działania . To, a także fakt, że JDK nie wypełnił NPE ciągiem komunikatów, stworzył silny negatywny sentyment, który nie jest dobrze uzasadniony. Rzeczywiście, różnica między NPE i IAE z perspektywy środowiska uruchomieniowego jest ściśle nazwą. Z tego punktu widzenia, im bardziej precyzyjne jest imię, tym bardziej wyraźne jest połączenie z dzwoniącym.

Christopher Smith
źródło
Różnica między większością niesprawdzonych wyjątków to tylko nazwa.
Thorbjørn Ravn Andersen
20

To pytanie w stylu „Świętej Wojny”. Innymi słowy, obie alternatywy są dobre, ale ludzie będą mieli swoje preferencje, których będą bronić na śmierć.

Steve McLeod
źródło
Nie, jest tylko jedna prawidłowa odpowiedź na to pytanie, a mianowicie użycie wyjątku IllegalArgument, gdy dane wejściowe do metody są niepoprawne. Również w środowisku
deweloperskim
@ Mr.Q I myślę, że NullPointerExceptionnależy to rzucić: to konwencja używana przez JDK i wymagająca interfejsów, jest bardziej szczegółowa (tak jak IndexOutOfBoundsException, itp.) Itp.
Solomon Ucko
kekekekkek ...: D
Yousha Aleayoub
17

Jeśli jest to settermetoda i nulljest do niej przekazywana, myślę, że sensowniej byłoby rzucić IllegalArgumentException. NullPointerExceptionWydaje się mieć więcej sensu w przypadku, gdy jesteś rzeczywiście próbuje użyć null.

Tak więc, jeśli używasz go i to null, NullPointer. Jeśli to były przekazywane i to null, IllegalArgument.

Jeremy Privett
źródło
9

Apache Commons Lang ma wyjątek NullArgumentException, który robi wiele rzeczy omówionych tutaj: rozszerza IllegalArgumentException, a jego jedyny konstruktor przyjmuje nazwę argumentu, który powinien mieć wartość inną niż null.

Chociaż wydaje mi się, że rzucenie czegoś takiego jak NullArgumentException lub IllegalArgumentException dokładniej opisuje wyjątkowe okoliczności, ja i moi koledzy postanowiliśmy zastosować się do porady Blocha na ten temat.

Brian T. Grant
źródło
7
Uwaga: usunęli go z commons-lang3: apache-commons.680414.n4.nabble.com/…
artbristol
7

Nie mogłem się bardziej zgodzić z tym, co zostało powiedziane. Niepowodzenie wcześnie, szybkie zawodzenie. Całkiem dobra mantra wyjątkowa.

Pytanie o to, który wyjątek rzucić, zależy głównie od osobistego gustu. Moim zdaniem IllegalArgumentException wydaje się bardziej konkretny niż używanie NPE, ponieważ mówi mi, że problem dotyczył argumentu przekazanego do metody, a nie wartości, która mogła zostać wygenerowana podczas wykonywania metody.

Moje 2 centy

Allain Lalonde
źródło
7

W rzeczywistości kwestia wyrzucenia IllegalArgumentException lub NullPointerException jest moim skromnym zdaniem jedynie „świętą wojną” dla mniejszości z niepełnym zrozumieniem obsługi wyjątków w Javie. Ogólnie zasady są proste i wyglądają następująco:

  • naruszenia ograniczeń argumentów muszą być wskazane tak szybko, jak to możliwe (-> szybki błąd), aby uniknąć nielegalnych stanów, które są znacznie trudniejsze do debugowania
  • w przypadku nieprawidłowego wskaźnika zerowego z jakiegokolwiek powodu, wyrzuć NullPointerException
  • w przypadku niedozwolonego indeksu tablic / kolekcji, wyrzuć ArrayIndexOutOfBounds
  • w przypadku ujemnej wielkości tablicy / kolekcji, wyrzuć NegativeArraySizeException
  • w przypadku nielegalnego argumentu, który nie jest objęty powyższym i dla którego nie masz innego bardziej szczegółowego typu wyjątku, wyrzuć IllegalArgumentException jako kosz na śmieci
  • z drugiej strony, w przypadku naruszenia ograniczenia W OBSZARZE, którego nie można uniknąć przez szybką awarię z jakiegoś ważnego powodu, przechwyć i zwróć jako IllegalStateException lub bardziej konkretnie sprawdzony wyjątek. Nigdy nie przepuszczaj oryginalnego NullPointerException, ArrayIndexOutOfBounds itp. W tym przypadku!

Istnieją co najmniej trzy bardzo dobre powody, dla których nie należy mapować wszelkiego rodzaju naruszeń ograniczeń argumentów na IllegalArgumentException, przy czym trzeci prawdopodobnie jest tak poważny, że oznacza praktykę złym stylem:

(1) Programista nie może bezpiecznie założyć, że wszystkie przypadki naruszenia ograniczeń argumentów powodują IllegalArgumentException, ponieważ znaczna większość klas standardowych używa tego wyjątku raczej jako kosza na śmieci, jeśli nie ma dostępnego bardziej konkretnego rodzaju wyjątku. Próba odwzorowania wszystkich przypadków naruszenia ograniczeń argumentów na IllegalArgumentException w interfejsie API prowadzi tylko do frustracji programistów podczas korzystania z klas, ponieważ biblioteki standardowe przeważnie stosują różne reguły, które naruszają wasze, a większość użytkowników interfejsu API również ich używa!

(2) Odwzorowanie wyjątków prowadzi do różnego rodzaju anomalii spowodowanych pojedynczym dziedziczeniem: Wszystkie wyjątki Javy są klasami i dlatego obsługują tylko pojedyncze dziedziczenie. Dlatego nie ma sposobu na utworzenie wyjątku, który naprawdę mówi zarówno o NullPointerException, jak i IllegalArgumentException, ponieważ podklasy mogą dziedziczyć tylko z jednego lub drugiego. Zgłoszenie wyjątku IllegalArgumentException w przypadku argumentu zerowego utrudnia zatem użytkownikom API rozróżnienie problemów, ilekroć program próbuje programowo rozwiązać problem, na przykład poprzez podanie wartości domyślnych do powtórzenia wywołania!

(3) Mapowanie faktycznie stwarza niebezpieczeństwo maskowania błędów: Aby zamapować naruszenia ograniczeń argumentów na IllegalArgumentException, musisz zakodować zewnętrzny try-catch w każdej metodzie, która ma dowolne ograniczone argumenty. Jednak po prostu przechwytywanie wyjątku RuntimeException w tym bloku catch nie wchodzi w rachubę, ponieważ ryzykuje to mapowaniem udokumentowanych wyjątków RuntimeException zgłaszanych przez metody libery stosowane w twoim przypadku do IllegalArgumentException, nawet jeśli nie są one spowodowane przez naruszenie ograniczeń argumentów. Musisz więc być bardzo konkretny, ale nawet ten wysiłek nie chroni cię przed przypadkowym odwzorowaniem nieudokumentowanego wyjątku wykonawczego innego API (tj. Błędu) na IllegalArgumentException twojego API.

Z drugiej strony, przy standardowej praktyce, reguły pozostają proste, a wyjątek powoduje, że pozostają zdemaskowane i szczegółowe. W przypadku metody wywołującej zasady są również łatwe: - jeśli napotkasz udokumentowany wyjątek czasu wykonywania, ponieważ przekazałeś niedozwoloną wartość, powtórz wywołanie z wartością domyślną (w tym przypadku wyjątki są konieczne) lub popraw kod - jeśli z drugiej strony napotkasz wyjątek czasu wykonywania, który nie został udokumentowany dla danego zestawu argumentów, zgłoś błąd twórcom metody, aby upewnić się, że ich kod lub dokumentacja zostały naprawione.

Sascha Baumeister
źródło
6

Przyjęta praktyka polegająca na wykorzystaniu IllegalArgumentException (komunikat String) do zadeklarowania parametru jako niepoprawnego i podania jak największej liczby szczegółów ... Aby więc powiedzieć, że znaleziono parametr zerowy, a wyjątek inny niż null, zrobiłbyś coś lubię to:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

Nie masz praktycznie żadnego powodu, aby pośrednio używać „NullPointerException”. Wyjątek NullPointerException to wyjątek zgłaszany przez wirtualną maszynę Java podczas próby wykonania kodu przy odwołaniu zerowym (jak toString () ).

Claude Houle
źródło
6

Zgłoszenie wyjątku, który jest wyłączny dla nullargumentów (niezależnie od tego, NullPointerExceptionczy jest to typ niestandardowy), nullzwiększa niezawodność automatycznego testowania. To zautomatyzowane testowanie można zrobić z refleksji i zbioru wartości domyślnych, jak w Guava s” NullPointerTester. Na przykład NullPointerTesterpróbowałby wywołać następującą metodę ...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

... z dwiema listami argumentów: "", nulli null, ImmutableList.of(). Sprawdziłby, że każde z tych połączeń generuje oczekiwane NullPointerException. Dla tej realizacji, przekazując nulllisty nie nie produkują NullPointerException. Jednak zdarza się, że generuje, IllegalArgumentExceptionponieważ NullPointerTesterzdarza się, że używa domyślnego ciągu "". Jeśli NullPointerTesteroczekuje tylko NullPointerExceptionna nullwartości, to złapie bakcyla. Jeśli się spodziewa IllegalArgumentException, tęskni.

Chris Povirk
źródło
5

Niektóre kolekcje zakładają, że nulljest odrzucane za pomocą NullPointerExceptionzamiast IllegalArgumentException. Na przykład, jeśli porównasz zestaw zawierający nullzestaw, który odrzuca null, pierwszy zestaw wywoła containsAlldrugi i złapie jego NullPointerException- ale nie IllegalArgumentException. (Patrzę na wdrożenie AbstractSet.equals.)

Można zasadnie argumentować, że stosowanie w ten sposób niesprawdzonych wyjątków jest antypatternem, że porównywanie kolekcji zawierających nullkolekcje, które nie mogą zawierać, nulljest prawdopodobnym błędem, który naprawdę powinien powodować wyjątek, lub że nullw ogóle umieszczanie kolekcji jest złym pomysłem . Niemniej jednak, jeśli nie jesteś skłonny powiedzieć, że equalsw takim przypadku należy wprowadzić wyjątek, utkniesz w pamięci, że NullPointerExceptionjest to wymagane w pewnych okolicznościach, ale nie w innych. („IAE przed NPE, z wyjątkiem po„ c ”...”)

Chris Povirk
źródło
Nie widzę, jak zawiera rzuca NPE w zależności od elementu w kolekcji. Jedynym powodem, dla którego NPE jest wyrzucany (przypadkowo), jest to, że sama kolekcja jest pusta (w takim przypadku NPE jest wyrzucany, ponieważ próbuje uzyskać dostęp do iteratora). Rodzi to jednak pytanie, czy należy sprawdzić wejściowe wartości zerowe, czy też powinno się je propagować, dopóki nie spróbuje uzyskać dostępu.
Alowaniak
2
new TreeSet<>().containsAll(Arrays.asList((Object) null));wyrzuca, NPEponieważ Listzawiera null.
Chris Povirk
1
Ach, w rzeczy samej, TreeSet # zawiera wyrzuca NPE „jeśli określony element ma wartość null, a ten zestaw używa naturalnego uporządkowania lub jego komparator nie pozwala na elementy null”. Patrzyłem tylko na AbstractSet, który dopuszcza zero, mój zły. Osobiście uważam, że to dziwne, że nie zwraca ono tylko fałszu, ponieważ w takim przypadku nie można dodać wartości null.
Alowaniak
5

Jako subiektywne pytanie należy to zamknąć, ale ponieważ nadal jest otwarte:

Jest to część polityki wewnętrznej stosowanej w moim poprzednim miejscu zatrudnienia i działała naprawdę dobrze. To wszystko z pamięci, więc nie pamiętam dokładnego sformułowania. Warto zauważyć, że nie korzystali ze sprawdzonych wyjątków, ale to wykracza poza zakres pytania. Niesprawdzone wyjątki, z których korzystali, dzieliły się na 3 główne kategorie.

NullPointerException: Nie rzucaj celowo. NPE mają być wyrzucane tylko przez maszynę wirtualną, gdy odwołuje się odwołanie zerowe. Należy dołożyć wszelkich starań, aby nigdy nie zostały wyrzucone. @Nullable i @NotNull powinny być używane w połączeniu z narzędziami do analizy kodu, aby znaleźć te błędy.

IllegalArgumentException: Zgłaszany, gdy argument funkcji nie jest zgodny z dokumentacją publiczną, dzięki czemu można zidentyfikować błąd i opisać go za pomocą przekazanych argumentów. Sytuacja PO mieści się w tej kategorii.

IllegalStateException: Zgłaszany, gdy funkcja jest wywoływana, a jej argumenty są albo nieoczekiwane w momencie ich przekazania, albo niezgodne ze stanem obiektu, do którego należy metoda.

Na przykład były dwie wewnętrzne wersje IndexOutOfBoundsException używane w rzeczach o długości. Jedna podklasa IllegalStateException, używana, jeśli indeks był większy niż długość. Druga podklasa IllegalArgumentException, używana, jeśli indeks był ujemny. Stało się tak, ponieważ można było dodać więcej elementów do obiektu, a argument byłby prawidłowy, a liczba ujemna nigdy nie jest poprawna.

Jak już powiedziałem, ten system działa naprawdę dobrze i trzeba było wyjaśnić, dlaczego istnieje takie rozróżnienie: „W zależności od rodzaju błędu łatwo jest wymyślić, co należy zrobić. Nawet jeśli tak naprawdę nie możesz dowiedzieć się, co poszło nie tak, możesz dowiedzieć się, gdzie można złapać ten błąd i utworzyć dodatkowe informacje debugowania ”.

NullPointerException: Obsługuj przypadek Null lub wstaw asercję, aby NPE nie został zgłoszony. Jeśli włożysz twierdzenie, to tylko jeden z dwóch pozostałych typów. Jeśli to możliwe, kontynuuj debugowanie tak, jakby to stwierdzenie było w pierwszej kolejności.

IllegalArgumentException: masz coś złego na stronie połączeń. Jeśli przekazywane wartości pochodzą z innej funkcji, dowiedz się, dlaczego otrzymujesz niepoprawną wartość. Jeśli przekazujesz jeden z argumentów propagujących, błąd sprawdza stos wywołań, aż znajdziesz funkcję, która nie zwraca oczekiwanej wartości.

IllegalStateException: Nie wywołałeś swoich funkcji w prawidłowej kolejności. Jeśli używasz jednego z argumentów, sprawdź je i wyślij IllegalArgumentException opisujący problem. Następnie możesz rozłożyć policzki na stosie, aż znajdziesz problem.

W każdym razie jego celem było to, że można kopiować tylko IllegalArgumentAssertions na stos. Nie ma możliwości propagowania wyjątków IllegalStateExceptions lub NullPointerExceptions na stosie, ponieważ miały one coś wspólnego z twoją funkcją.

Ben Seidel
źródło
4

Ogólnie rzecz biorąc, programista nigdy nie powinien zgłaszać wyjątku NullPointerException. Ten wyjątek jest zgłaszany przez środowisko wykonawcze, gdy kod próbuje wyrejestrować zmienną o wartości null. Dlatego jeśli twoja metoda chce jawnie zabronić wartości null, w przeciwieństwie do sytuacji, w której wartość null wywołuje wyjątek NullPointerException, powinieneś zgłosić wyjątek IllegalArgumentException.


źródło
9
JavaDoc na NPE ma inną opinię: „Aplikacje powinny zgłaszać instancje tej klasy w celu wskazania innych nielegalnych zastosowań obiektu zerowego.”. Nie bądź taki kategoryczny
Donz
4

Chciałem wyróżnić argumenty Null spośród innych nielegalnych argumentów, więc wyprowadziłem wyjątek z IAE o nazwie NullArgumentException. Nie muszę nawet czytać komunikatu wyjątku, wiem, że do metody przekazano argument zerowy, a czytając komunikat, dowiaduję się, który argument był pusty. Nadal wychwytuję wyjątek NullArgumentException za pomocą procedury obsługi IAE, ale w moich logach szybko widzę różnicę.

Jason Fritcher
źródło
Przyjąłem podejście „wyrzuć nowe IllegalArgumentException („ foo == null ”)”. W każdym razie musisz zalogować nazwę zmiennej (aby mieć pewność, że patrzysz na odpowiednie stanowisko itp.)
Thorbjørn Ravn Andersen
4

dychotomia ... Czy się nie pokrywają? Tylko dublotomia mogą się nakładać tylko na nie nakładające się części całości. Jak to widze:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));
Luis Daniel Mesa Velasquez
źródło
1
Podwoiłoby to koszty tworzenia wyjątków i tak naprawdę nie pomogłoby, ponieważ łapanie NullPointerExceptionnic nie zrobiłoby. Jedyną rzeczą, która może pomóc, jest to IllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException, że nie mamy wielokrotnego dziedziczenia.
maaartinus
Uważam, że bardziej szczegółowe wyjątki powinny obejmować bardziej ogólne wyjątki. NPE jest dla wyrażenia, a IAE dla metody. Ponieważ metody zawierają instrukcje zawierające wyrażenia, IAE jest bardziej ogólne.
Sgene9
Jeśli chodzi o koszty ogólne, nie mam pojęcia. Ale ponieważ ślady stosu byłyby w zasadzie identyczne, z wyjątkiem tego, że nazwa wyjątku zmieniła się w środku, myślę, że nie powinno być zbyt dużego obciążenia dla podwójnych wyjątków. Jeśli ktoś martwi się narzutem, może użyć instrukcji „if”, aby zwrócić wartość zerową lub -1 zamiast zgłaszania wyjątku.
Sgene9
4

NullPointerExceptionwyrzucane podczas próby uzyskania dostępu do obiektu za pomocą zmiennej referencyjnej, której bieżącą wartością jest null.

IllegalArgumentException wyrzucany, gdy metoda odbiera argument sformatowany inaczej niż oczekuje metoda.

Nitesh Soomani
źródło
3

Według twojego scenariusza IllegalArgumentExceptionjest najlepszym wyborem, ponieważ nullnie jest prawidłową wartością dla Twojej nieruchomości.

Lew
źródło
0

W idealnym przypadku wyjątki czasu wykonywania nie powinny być zgłaszane. Dla Twojego scenariusza należy utworzyć sprawdzony wyjątek (wyjątek biznesowy). Ponieważ jeśli którykolwiek z tych wyjątków zostanie zgłoszony i zarejestrowany, wprowadzi w błąd programistę podczas przeglądania dzienników. Zamiast tego wyjątki biznesowe nie powodują tej paniki i zwykle są ignorowane podczas rozwiązywania problemów z dziennikami.

Vijay
źródło
-1

Definicje z łączy do dwóch powyższych wyjątków to IllegalArgumentException: Zgłoszony w celu wskazania, że ​​do metody przekazano niedozwolony lub nieodpowiedni argument. NullPointerException: Zgłaszany, gdy aplikacja próbuje użyć wartości null w przypadku, gdy wymagany jest obiekt.

Dużą różnicą tutaj jest to, że IllegalArgumentException powinien być używany podczas sprawdzania poprawności argumentu dla metody. NullPointerException ma być używany za każdym razem, gdy obiekt jest „używany”, gdy jest pusty.

Mam nadzieję, że to pomoże spojrzeć na te dwa z perspektywy.

martinatime
źródło
1
Istotny bit polega na tym, że to aplikacja używa wartości null, a nie środowisko wykonawcze. Tak więc zachodzi dość duże nakładanie się między „gdy przekazano metodę niedozwolony lub nieodpowiedni argument” a „gdy aplikacja używa wartości null”. Teoretycznie, jeśli aplikacja przekroczy wartość null dla pola wymagającego wartości innej niż null, oba kryteria są spełnione.
Christopher Smith
-1

Jeśli jest to „ustawiający” lub gdzieś, z którego członka mogę skorzystać później, zwykle używam IllegalArgumentException.

Jeśli jest to coś, czego zamierzam teraz użyć (dereferencję) w tej metodzie, proaktywnie rzucam wyjątek NullPointerException. Podoba mi się to lepiej niż pozwalanie na to środowisku wykonawczemu, ponieważ mogę dostarczyć pomocnej wiadomości (wygląda na to, że środowisko wykonawcze też może to zrobić, ale to rant na kolejny dzień).

Jeśli przesłaniam metodę, używam tego, czego używa przesłonięta metoda.

erickson
źródło
-1

Powinieneś zgłosić IllegalArgumentException, ponieważ sprawi to, że programiści zrozumieją, że zrobił coś nieprawidłowego. Programiści są tak przyzwyczajeni do wyświetlania NPE przez maszynę wirtualną, że żaden programista nie od razu zda sobie sprawę z jego błędu i zacznie rozglądać się losowo lub, co gorsza, obwinia kod za buggy.

Will Sargent
źródło
4
Przepraszamy, jeśli programista rozgląda się „losowo” po otrzymaniu jakiegokolwiek wyjątku ... zmiana nazwy wyjątku nie pomoże.
Christopher Smith,
-1

W takim przypadku IllegalArgumentException przekazuje użytkownikowi za pomocą interfejsu API jasne informacje, że „nie powinno być zerowe”. Jak zauważyli inni użytkownicy forum, możesz użyć NPE, jeśli chcesz, o ile przekażesz odpowiednie informacje użytkownikowi za pomocą interfejsu API.

GaryF i tweakt porzucili referencje „Effective Java” (które przysięgam), które zalecają używanie NPE. A sprawdzenie, jak zbudowane są inne dobre interfejsy API, jest najlepszym sposobem, aby zobaczyć, jak zbudować interfejs API.

Innym dobrym przykładem jest spojrzenie na Spring API. Na przykład org.springframework.beans.BeanUtils.instantiateClass (Constructor ctor, Object [] args) ma linię Assert.notNull (ctor, „Constructor nie może mieć wartości null”). org.springframework.util.Assert.notNull (Object object, String message) metoda sprawdza, czy przekazany argument (obiekt) ma wartość NULL, a jeśli tak, zgłasza nowy wyjątek IllegalArgumentException (wiadomość), który jest następnie przechwytywany w org. metoda springframework.beans.BeanUtils.instantiateClass (...).


źródło
-5

Jeśli zdecydujesz się wyrzucić NPE i użyjesz argumentu w swojej metodzie, jawne sprawdzenie wartości null może być zbędne i kosztowne. Myślę, że VM już to dla ciebie robi.

Jassuncao
źródło
Czas wykonywania nie będzie zawierał znaczącej wiadomości.
mP.
Właściwie ten komentarz może wywodzić inną opinię. Pozwól VM zabrać głos, NPEa programiści zabiorą głos IAEprzed VM, jeśli chcą.
Jin Kwon
1
Kosztowny? Nie sądzę, aby == null było tak drogie ... Poza tym argument null może być po prostu zapisany do późniejszego użycia i wygeneruje wyjątek długo po wywołaniu metody, co utrudni śledzenie błędu. Lub możesz utworzyć kosztowny obiekt przed użyciem argumentu zerowego i tak dalej. Wczesne wykrycie wydaje się dobrym rozwiązaniem.
PhiLho
głosowanie w dół tej odpowiedzi jest niezrozumieniem stojącego za nią sprzętu. Z pewnością kontrole sprzętowe (które wykonuje procesor) są tańsze niż kontrole jawne. Dereferencjonowanie wartości null jest specjalnym przypadkiem SegmentationFault (SegV) (dostęp do strony nie będącej własnością procesu), który CPU / OS sprawdza i JRE obsługuje jako specjalny przypadek zgłaszający wyjątek NullPointerException.
digital_infinity