Widzę to @Nullable
i @Nonnull
adnotacje mogą być pomocne w zapobieganiu NullPointerException
s, ale nie rozprzestrzeniają się zbyt daleko.
- Skuteczność tych adnotacji spada całkowicie po jednym poziomie pośredniej, więc jeśli dodasz tylko kilka, nie będą one rozprzestrzeniać się zbyt daleko.
- Ponieważ te adnotacje nie są dobrze egzekwowane, istnieje niebezpieczeństwo, że wartość oznaczona znakiem
@Nonnull
nie jest zerowa, a co za tym idzie, nie będzie przeprowadzać kontroli zerowej.
Poniższy kod powoduje, że parametr oznaczony symbolem @Nonnull
jest null
bez zgłaszania zastrzeżeń. Rzuca, NullPointerException
gdy jest uruchomiony.
public class Clazz {
public static void main(String[] args){
Clazz clazz = new Clazz();
// this line raises a complaint with the IDE (IntelliJ 11)
clazz.directPathToA(null);
// this line does not
clazz.indirectPathToA(null);
}
public void indirectPathToA(Integer y){
directPathToA(y);
}
public void directPathToA(@Nonnull Integer x){
x.toString(); // do stuff to x
}
}
Czy istnieje sposób na bardziej rygorystyczne egzekwowanie i / lub dalsze rozpowszechnianie tych adnotacji?
java
annotations
nullpointerexception
nullable
code-standards
Mike Rylander
źródło
źródło
@Nullable
lub@Nonnull
, ale jeśli są tego warte, jest bardzo „prawdopodobne, że zachęcą do debaty”@Nonnull
podczas wywoływania@Nonnull
metody ze zmienną dopuszczającą wartość null. Oczywiście rzutowanie z adnotacją nie jest możliwe w Javie 7, ale Java 8 będzie dodawać możliwość stosowania adnotacji do użycia zmiennej, w tym rzutowania. Więc może to stać się możliwe do zaimplementowania w Javie 8.(@NonNull Integer) y
jest syntaktycznie możliwe, ale kompilator nie może emitować żadnego określonego kodu bajtowego na podstawie adnotacji. W przypadku asercji w czasie wykonywania wystarczające są małe metody pomocnicze, omówione w bugs.eclipse.org/442103 (np.directPathToA(assertNonNull(y))
) - ale pamiętaj, to tylko pomaga szybko zawieść. Jedynym bezpiecznym sposobem jest wykonanie rzeczywistego sprawdzenia wartości null (oraz, miejmy nadzieję, alternatywnej implementacji w gałęzi else).@Nonnull
io@Nullable
której mówisz, ponieważ istnieje wiele podobnych adnotacji (patrz to pytanie ). Czy mówisz o adnotacjach w pakieciejavax.annotation
?Odpowiedzi:
Krótka odpowiedź: myślę, że te adnotacje są przydatne tylko dla twojego IDE, aby ostrzec cię o potencjalnie zerowych błędach wskaźnika.
Jak powiedziano w książce „Wyczyść kod”, powinieneś sprawdzić parametry swojej metody publicznej, a także unikać sprawdzania niezmienników.
Kolejną dobrą wskazówką jest to, że nigdy nie zwraca wartości null, ale zamiast tego używa się wzorca obiektu Null .
źródło
Optional
typu zamiast zwykłegonull
Optional
i dopuszcza wartość null w tym przypadku polega na tym, żeOptional
lepiej informuje, że ta wartość może celowo być pusta. Z pewnością nie jest to magiczna różdżka i w środowisku wykonawczym może zawieść dokładnie tak samo, jak zmienna dopuszczająca wartość null. JednakOptional
moim zdaniem odbiór API przez programistę jest lepszy .Poza tym, że IDE daje wskazówki, gdy przechodzisz
null
do metod, które oczekują, że argument nie będzie zerowy, istnieją dalsze zalety:Dzięki temu Twój kod będzie łatwiejszy w utrzymaniu (ponieważ nie potrzebujesz
null
sprawdzania) i mniej podatny na błędy.źródło
assert
. Znajduję@Nullable
i jestem@Nonnull
pożytecznymi pomysłami, ale wolałbym więcej siły za nimi, niż stawiać hipotezę, co można z nimi zrobić, co nadal pozostawia otwartą możliwość nic z nimi nie robić.Myślę, że to oryginalne pytanie pośrednio wskazuje na ogólne zalecenie, że sprawdzanie wskaźnika zerowego w czasie wykonywania jest nadal potrzebne, mimo że używane jest @NonNull. Skorzystaj z następującego łącza:
Nowe adnotacje typu Java 8
W powyższym blogu zaleca się:
źródło
Kompilując oryginalny przykład w Eclipse przy zgodności 1.8 i z włączoną analizą null opartą na adnotacjach, otrzymujemy następujące ostrzeżenie:
To ostrzeżenie jest sformułowane analogicznie do tych ostrzeżeń, które pojawiają się podczas mieszania wygenerowanego kodu ze starszym kodem przy użyciu typów surowych („konwersja niesprawdzona”). Tutaj mamy dokładnie taką samą sytuację: metoda
indirectPathToA()
ma "legacy" podpis, ponieważ nie określa żadnego zerowego kontraktu. Narzędzia mogą z łatwością to zgłosić, więc będą ścigać Cię po wszystkich zaułkach, w których należy propagować adnotacje o wartości NULL, ale jeszcze nie jest.A kiedy używamy sprytnego
@NonNullByDefault
, nie musimy nawet tego mówić za każdym razem.Innymi słowy: to, czy adnotacje o wartości zerowej „rozprzestrzeniają się bardzo daleko”, może zależeć od używanego narzędzia i od tego, jak rygorystycznie przestrzegasz wszystkich ostrzeżeń wysyłanych przez to narzędzie. Dzięki adnotacjom o wartości NULL TYPE_USE możesz wreszcie pozwolić narzędziu ostrzec Cię o każdym możliwym NPE w programie, ponieważ wartość zerowa stała się nieodłączną właściwością systemu typów.
źródło
Zgadzam się, że adnotacje „nie rozprzestrzeniają się zbyt daleko”. Widzę jednak błąd po stronie programisty.
Nonnull
Adnotację rozumiem jako dokumentację. Poniższa metoda wyraża, że wymaga (jako warunku wstępnego) argumentu innego niż nullx
.Poniższy fragment kodu zawiera błąd. Metoda wywołuje
directPathToA()
bez wymuszania, żey
jest różna od null (to znaczy nie gwarantuje warunku wstępnego wywoływanej metody). Jedną z możliwości jest dodanieNonnull
adnotacji również doindirectPathToA()
(propagowanie warunku wstępnego). Możliwość druga polega na sprawdzeniu nieważnościy
inindirectPathToA()
i uniknięciu wywołaniadirectPathToA()
kiedyy
jest zerowe.źródło
@Nonnull
co ma,indirectPathToA(@Nonnull Integer y)
jest złą praktyką IMHO: będziesz musiał zachować propagację na pełnym stosie wywołań (więc jeśli dodasznull
check indirectPathToA()
, będziesz musiał zastąpić@Nonnull
przez@Nullable
w pełnym stosie wywołań). W przypadku dużych aplikacji oznaczałoby to ogromne nakłady na konserwację.To, co robię w moich projektach, to aktywowanie następującej opcji podczas inspekcji kodu „Stałe warunki i wyjątki”:
Zaproponuj adnotację @Nullable dla metod, które mogą zwracać wartość null i zgłaszać wartości null przekazywane do parametrów bez adnotacji
Po aktywacji wszystkie parametry bez adnotacji będą traktowane jako niezerowe, a zatem zobaczysz również ostrzeżenie dotyczące połączenia pośredniego:
Dla jeszcze silniejszych testów dobrym wyborem może być Checker Framework (zobacz ten fajny samouczek .
Uwaga : jeszcze go nie używałem i mogą wystąpić problemy z kompilatorem Jacka: zobacz ten raport o błędzie
źródło
W Javie użyłbym opcjonalnego typu Guava . Będąc rzeczywistym typem, kompilator gwarantuje jego użycie. Łatwo go ominąć i uzyskać
NullPointerException
, ale przynajmniej podpis metody jasno komunikuje, czego oczekuje jako argument lub co może zwrócić.źródło
java.util.Optional
zamiast klasy Guava. Zobacz notatki / porównanie Guavy, aby uzyskać szczegółowe informacje na temat różnic.Jeśli używasz Kotlin, obsługuje on te adnotacje o wartości zerowej w swoim kompilatorze i zapobiega przekazywaniu wartości null do metody Java, która wymaga argumentu innego niż null. Chociaż to pytanie było pierwotnie skierowane do Javy, wspominam o tej funkcji Kotlin, ponieważ jest ona specjalnie ukierunkowana na te adnotacje Java, a pytanie brzmiało: „Czy istnieje sposób, aby te adnotacje były bardziej rygorystycznie egzekwowane i / lub dalej rozpowszechniane?” i ta funkcja sprawia, że te adnotacje są bardziej rygorystyczne .
Klasa Java za pomocą
@NotNull
adnotacjiKlasa Kotlin wywołująca klasę Javy i przekazująca wartość null dla argumentu z adnotacją @NotNull
Błąd kompilatora Kotlin wymuszający
@NotNull
adnotację.zobacz: http://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
źródło
myString.hashCode()
nadal będzie rzucał NPE, nawet jeśli@NotNull
nie jest dodany w parametrze. Więc co jest bardziej konkretnego w dodawaniu go?Ponieważ nowa funkcja Java 8 jest opcjonalna , nie należy już używać @Nullable ani @Notnull we własnym kodzie . Weźmy poniższy przykład:
Można go przepisać za pomocą:
Użycie opcjonalnego wymusza sprawdzenie wartości null . W powyższym kodzie dostęp do wartości można uzyskać tylko przez wywołanie
get
metody.Kolejną zaletą jest to, że kod staje się bardziej czytelny . Po dodaniu Java 9 ifPresentOrElse funkcję można by nawet zapisać jako:
źródło
Optional
przypadku wciąż istnieje wiele bibliotek i struktur, które używają tych adnotacji, tak że nie jest możliwe zaktualizowanie / zastąpienie wszystkich zależności wersjami zaktualizowanymi do korzystania z opcji.Optional
może jednak pomóc w sytuacjach, w których używasz null we własnym kodzie.