Czy lepiej jest użyć assert lub IllegalArgumentException dla wymaganych parametrów metody?

87

W Javie, co jest bardziej zalecane i dlaczego? Oba typy będą zgłaszać wyjątki, więc ich obsługa jest taka sama. assertjest nieco krótszy, ale nie jestem pewien, ile to ma znaczenie.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

vs

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
źródło
Wolę prosty obj.hashCode();-)
Marco

Odpowiedzi:

117

STRZEC SIĘ!

Asercje są usuwane w czasie wykonywania, chyba że jawnie określisz opcję „włącz asercje” podczas kompilowania kodu. Asercji Java nie należy używać w kodzie produkcyjnym i należy ją ograniczyć do metod prywatnych (patrz Wyjątek vs. asercja ), ponieważ oczekuje się, że metody prywatne będą znane i używane tylko przez programistów. Również assertrzuci AssertionError która rozciąga się Errornie Exception, a które zazwyczaj oznacza, że masz bardzo nienormalne błąd (jak „OutOfMemoryError”, które jest trudne do odzyskania od, prawda?), Nie oczekuje się, aby móc leczyć.

Usuń flagę „włącz asercje” i sprawdź za pomocą debugera, a zobaczysz, że nie nadepniesz na wywołanie wyrzucenia IllegalArgumentException ... ponieważ ten kod nie został skompilowany (ponownie po usunięciu „ea”)

Lepiej jest użyć drugiej konstrukcji do metod publicznych / chronionych, a jeśli chcesz czegoś, co jest zrobione w jednym wierszu kodu, jest co najmniej jeden sposób, który znam. Ja osobiście korzystam z klasy Spring Framework , Assertktóra ma kilka metod sprawdzania argumentów i która generuje błąd „IllegalArgumentException” w przypadku niepowodzenia. Zasadniczo to, co robisz, to:

Assert.notNull(obj, "object was null");

... Który faktycznie wykona dokładnie ten sam kod, który napisałeś w drugim przykładzie. Istnieje kilka innych przydatnych metod, takich jak hasText, hasLengthtam.

Nie lubię pisać więcej kodu niż to konieczne, więc cieszę się, gdy zmniejszam liczbę zapisanych linii o 2 (2 linie> 1 linia) :-)

Jalayn
źródło
Ach, zapomniałem o usunięciu twierdzeń! Świetna odpowiedź. Poczekam chwilę, aby zobaczyć, czy coś jeszcze się pojawi, a następnie zaakceptuję :)
Daenyth,
2
Zauważ, że nie ma flagi, która usuwa asercje w czasie kompilacji (chociaż można je usunąć poprzez kompilację warunkową ). Asercje są domyślnie wyłączone w czasie wykonywania (myślę, że JVM traktuje je jako NOOP), ale można je włączyć java -eai programowo. @Jalayn Myślę, że posiadanie asercji w kodzie produkcyjnym jest całkowicie poprawne, ponieważ są one przydatne do debugowania w terenie
Justin Muller
@Jalayn, -1. Kompilator nie usuwa kodu asercji. Nawet jeśli nie zostaną uruchomione, chyba że wykonasz polecenie cmd java -ea.
Pacerier
5
nie ma potrzeby korzystania z frameworka wiosennego, gdy można go użyćObjects.requreNonNull
kambuzujący 10.10.17
46

Musisz użyć wyjątku. Użycie asercji byłoby niewłaściwym użyciem tej funkcji.

Niezaznaczone wyjątki służą do wykrywania błędów programowych użytkowników biblioteki, podczas gdy twierdzenia mają na celu wykrywanie błędów we własnej logice. Są to osobne problemy, których nie należy mieszać.

Na przykład twierdzenie

assert myConnection.isConnected();

oznacza „Wiem, że każda ścieżka kodu prowadząca do tego stwierdzenia zapewnia, że myConnectionjest on połączony; jeśli powyższy kod nie uzyskał prawidłowego połączenia, powinien zgłosić wyjątek lub powrócić przed osiągnięciem tego punktu”.

Z drugiej strony czek

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

oznacza, że ​​„Wywołanie mojej biblioteki bez nawiązania połączenia jest błędem programowania”.

dasblinkenlight
źródło
1
Ta informacja jest naprawdę pomocna, ale akceptuję Jalayna, ponieważ wskazuje, jak potencjalnie mógłbym wprowadzić błąd za pomocą metody assert.
Daenyth
4
doskonały punkt „Niezaznaczone wyjątki służą do wykrywania błędów programowych użytkowników biblioteki, podczas gdy twierdzenia mają na celu wykrywanie błędów we własnej logice. Są to osobne problemy, których nie należy mieszać”.
Asim Ghaffar,
To prawda, ale zakłada się, że OP pisze bibliotekę. Jeśli ich kod przeznaczony jest wyłącznie do użytku wewnętrznego, akceptowalne jest twierdzenie.
user949300,
11

Jeśli piszesz funkcję, która nie dopuszcza nulljako prawidłowej wartości parametru, należy dodać @Nonnulladnotację do podpisu i użyć, Objects.requireNonNullaby sprawdzić, czy argument jest, nulli wyrzucić, NullPointerExceptionjeśli tak jest. @NonnullAdnotacja jest dla dokumentacji i dostarczy pomocnych ostrzeżeń w czasie kompilacji w niektórych przypadkach. Nie zapobiega nullprzekazywaniu w czasie wykonywania.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Aktualizacja:@Nonnull adnotacja nie jest częścią standardowej biblioteki języka Java. Zamiast tego istnieje wiele konkurujących standardów z bibliotek stron trzecich (patrz Której adnotacji @NotNull Java należy użyć? ). To nie znaczy, że używanie go jest kiepskim pomysłem, tylko że nie jest to standard.

kambunkcyjny
źródło
Dzięki za wskazanie Objects.requireNonNull(), co było dla mnie nowe. Czy wiesz, czy jest gdzieś podobna metoda „needTrue ()” lub „requEqual ()”? Nic wbrew twierdzeniom Springa, ale nie wszyscy tego używają.
user949300,
@ user949300 Objects.requireNonNull()jest do sprawdzania poprawności argumentów. Jeśli argument musi być truerówny lub równy, argument jest bezcelowy. W przypadkach, o błędach innych niż nielegalnych argumentów, należy które dokładniej opisuje błąd. Istnieje również JUnit, ale to jest do testów. throwExceptionAssert
kambuzowy
Myślałem bardziej jak, powiedzmy, dla funkcji pierwiastka kwadratowego Objects.requireTrue(x >= 0.0);lub jakiegoś skrótu,Objects.requireEquals(hash.length == 256)
user949300,
Z którego @Nonnull muszę korzystać? javax.validation.constraints.NotNull?
Aguid
Chciałbym użyć adnotacji Eclipse JDT , ponieważ są one wykonane przez sprytne chłopaki :). Dokumentacja: help.eclipse.org/neon/… - Możesz również skonfigurować IntelliJ do ich używania.
koppor
2

Zawsze wolę rzucić IllegalArgumentException zamiast twierdzeń.

Asercje są używane głównie w JUnit lub innych narzędziach testujących do sprawdzania / potwierdzania wyników testów. Może to dawać fałszywe wrażenie innym programistom, że twoja metoda jest metodą testową.

Sensowne jest także zgłoszenie wyjątku IllegalArgumentException, gdy do metody przekazano niedozwolony lub niewłaściwy argument . Jest to bardziej zgodne z konwencją obsługi wyjątków stosowaną przez programistów Java.

rai.skumar
źródło
5
Asercje pojawiły się około 40 lat przed JUnit - zapytaj programistę C o makra ASSERT.
JBRWilkinson
3
to pytanie nie dotyczy c; chodzi o Javę. Odpowiedziałem w kontekście Java.
rai.skumar
Nie należy mylić asercji (słowa kluczowego zarezerwowanego) z Asersem (klasa JUnit), ponieważ oba służą do sprawdzania zmiennej, ale poza tym są to dwie bardzo różne rzeczy i zachowują się bardzo odmiennie.
Newtopian,
1

IMO drugi jest nieco lepszy, ponieważ przynosi więcej informacji i może być dalej rozszerzany (np. Poprzez rozszerzenie klasy wyjątków), aby był jeszcze bardziej pouczający, a także nie używa porównania negatywnego, które jest łatwiejsze do zrozumienia.

0lukasz0
źródło
1

Nie używam często asertów, ale wspólne podejście z Lombock @NonNull: https://projectlombok.org/features/NonNull

Implementacja Lombok: import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Wersja Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok to naprawdę niezła biblioteka, z której korzystam wszędzie

kensai
źródło