Niedawno natknąłem się na @SafeVarargs
adnotację java . Szukanie w Google, co sprawia, że funkcja wariadyczna w Javie jest niebezpieczna, wprawiło mnie w zakłopotanie (zatrucie sterty? Usunięte typy?), Więc chciałbym wiedzieć kilka rzeczy:
Co sprawia, że zmienna funkcja Java jest niebezpieczna w tym
@SafeVarargs
sensie (najlepiej wyjaśnione w formie szczegółowego przykładu)?Dlaczego ta adnotacja jest pozostawiona uznaniu programisty? Czy to nie jest coś, co kompilator powinien być w stanie sprawdzić?
Czy jest jakiś standard, do którego należy się stosować, aby jego funkcja była rzeczywiście bezpieczna dla waragów? Jeśli nie, jakie są najlepsze praktyki, aby to zapewnić?
retType myMethod(Arg first, Arg... others)
. Jeśli nie określiszfirst
, dozwolona jest pusta tablica i możesz mieć metodę o tej samej nazwie z tym samym typem zwracania, która nie pobiera żadnych argumentów, co oznacza, że maszyna JVM będzie miała trudności z określeniem, którą metodę należy wywołać.Odpowiedzi:
1) W Internecie i na StackOverflow jest wiele przykładów dotyczących konkretnego problemu z typami generycznymi i varargami. Zasadniczo dzieje się tak, gdy masz zmienną liczbę argumentów typu parametru typu:
W Javie varargs są cukrem składniowym, który podlega prostemu „przepisaniu” w czasie kompilacji: parametr varargs typu
X...
jest konwertowany na parametr typuX[]
; i za każdym razem, gdy wywoływana jest ta metoda varargs, kompilator zbiera wszystkie „argumenty zmiennych”, które znajdują się w parametrze varargs, i tworzy tablicę tak samo jaknew X[] { ...(arguments go here)... }
.Działa to dobrze, gdy typ varargs jest podobny do betonu
String...
. Kiedy jest to zmienna typuT...
, taka jak , działa również, gdyT
wiadomo, że jest konkretnym typem dla tego wywołania. np. jeśli powyższa metoda byłaby częścią klasyFoo<T>
i maszFoo<String>
odniesienie, to wywołaniefoo
jej byłoby w porządku, ponieważ wiemy, żeT
znajduje sięString
w tym punkcie kodu.Jednak nie działa, gdy „wartość”
T
jest parametrem innego typu. W Javie nie jest możliwe utworzenie tablicy składającej się z parametru typu type (new T[] { ... }
). Więc zamiast tego Java używanew Object[] { ... }
(tutajObject
jest górna granicaT
; gdyby górna granica była czymś innym, byłaby to zamiastObject
), a następnie wyświetla ostrzeżenie kompilatora.Więc co jest złego w tworzeniu
new Object[]
zamiastnew T[]
czy w czymkolwiek? Cóż, tablice w Javie znają swój typ komponentu w czasie wykonywania. Dlatego przekazany obiekt tablicy będzie miał nieprawidłowy typ komponentu w czasie wykonywania.Prawdopodobnie najbardziej powszechne użycie varargs, po prostu do iteracji po elementach, nie stanowi problemu (nie przejmujesz się typem wykonawczym tablicy), więc jest to bezpieczne:
Jednak w przypadku wszystkiego, co zależy od typu składnika środowiska wykonawczego przekazanej tablicy, nie będzie to bezpieczne. Oto prosty przykład czegoś, co jest niebezpieczne i ulega awarii:
Problem polega na tym, że zależymy od typu
args
byciaT[]
, aby zwrócić go jakoT[]
. Ale w rzeczywistości typ argumentu w czasie wykonywania nie jest wystąpieniemT[]
.3) Jeśli twoja metoda ma argument typu
T...
(gdzie T jest dowolnym parametrem typu), to:T
T[]
Rzeczy, które zależą od typu środowiska wykonawczego tablicy, obejmują: zwrócenie jej jako typu
T[]
, przekazanie go jako argumentu do parametru typuT[]
, pobranie typu tablicy przy użyciu.getClass()
, przekazanie do metod, które zależą od typu środowiska wykonawczego tablicy, takich jakList.toArray()
iArrays.copyOf()
itp.2) Rozróżnienie, o którym wspomniałem powyżej, jest zbyt skomplikowane, aby można je było łatwo rozróżnić automatycznie.
źródło
FOO<T extends Something>
czy byłoby bezpiecznie zwrócić T [] metody varargs jako tablicęSomthing
s?@SafeVarargs
to sytuacja, w której jedyną rzeczą, jaką robisz z tablicą, jest przekazanie jej do innej metody, która jest już tak opisana (na przykład: często piszę metody vararg, które istnieją, aby przekonwertować swój argument na listę przy użyciuArrays.asList(...)
i przekazać go do innej metody; taki przypadek zawsze można opatrzyć adnotacją,@SafeVarargs
ponieważArrays.asList
ma adnotację).@SafeVarargs
chyba że metoda jeststatic
lubfinal
, ponieważ w przeciwnym razie mogłaby zostać zastąpiona w klasie pochodnej czymś niebezpiecznym.private
metod, których również nie można przesłonić.Aby uzyskać najlepsze praktyki, rozważ to.
Jeśli masz to:
Zmień to na to:
Zauważyłem, że zwykle dodam varargy tylko po to, aby było wygodniej dla moich rozmówców. Dla mojej wewnętrznej implementacji prawie zawsze wygodniej byłoby użyć pliku
List<>
. Tak więc, aby wrócić naArrays.asList()
barana i upewnić się, że nie ma możliwości wprowadzenia Heap Pollution, oto co robię.Wiem, że to tylko odpowiada na twoje # 3. newacct udzielił świetnej odpowiedzi na punkty 1 i 2 powyżej, a nie mam wystarczającej reputacji, aby zostawić to w komentarzu. : P
źródło