1) Istnieje wiele przykładów w Internecie i na StackOverflow na temat konkretnego problemu z rodzajami i varargs. Zasadniczo, to jest, gdy masz zmienną liczbę argumentów typu typu parametru:
void foo(T... args);
W Javie varargs to cukier syntaktyczny że ulega proste „ponownego pisania” w czasie kompilacji: parametr varargs typu X...
jest konwertowany na parametr typu X[]
; i za każdym razem, gdy wywoływana jest ta metoda varargs, kompilator zbiera wszystkie "zmienne argumenty" znajdujące się w parametrze varargs i tworzy tablicę tak jak new X[] { ...(arguments go here)... }
.
Działa to dobrze, gdy typ varargs jest konkretny jak String...
. Gdy jest to zmienna typu, taka jak T...
, działa również wtedy, gdy wiadomo, że T
jest konkretnym typem dla tego połączenia. na przykład jeśli powyższa metoda była częścią klasy Foo<T>
, a użytkownik ma odwołanie Foo<String>
, wówczas wywołanie foo
na tym byłoby w porządku, ponieważ wiemy, że T
jest String
w tym miejscu kodu.
Jednak nie działa, gdy "wartość" T
jest parametrem innego typu. W języku Java niemożliwe jest utworzenie tablicy typu komponentu typu parametru (new T[] { ... }
). W związku z tym Java zamiast niego używa new Object[] { ... }
(tutaj Object
jest górną granicą T
; gdyby górna granica była czymś innym, byłoby to zamiast Object
), a następnie wyświetli ostrzeżenie kompilatora.
Co jest nie tak z tworzeniem new Object[]
zamiast new T[]
czy czego? Cóż, tablice w Javie znają swój typ komponentu w czasie wykonywania. Zatem przekazany obiekt tablicy będzie miał niewłaściwy typ komponentu w czasie wykonywania.
Dla prawdopodobnie najczęstszą wykorzystanie varargs, po prostu iteracyjne nad elementami, to nie jest problem (nie dbają o rodzaju wykonawczego tablicy), więc jest to bezpieczne:
@SafeVarargs
final void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Jednak dla wszystkiego, co zależy od typu składnika wykonawczego przekazywanej tablicy, nie będzie ona bezpieczna.Oto prosty przykład czegoś, co jest niebezpieczne i awarie:
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
Problem polega na tym, że są uzależnione od rodzaju args
być T[]
w celu przywrócenia go jako T[]
. Ale faktycznie typ argumentu w czasie wykonywania nie jest instancją T[]
.
3) Jeśli metoda ma argumentu typu T...
(gdzie t oznacza dowolny parametr typ), a następnie:
- Safe: Jeśli metoda zależy tylko na tym, że elementy tablicy są przypadki od
T
- niebezpieczne: Jeśli to polega na tym, że matryca jest wystąpienie
T[]
co jest zależne od rodzaju wykonawczego tablicy obejmują: powracającej jako typ T[]
, podając je jako argumentu do parametru typu T[]
, coraz rodzaj tablicy przy użyciu .getClass()
, przekazując je do metod, które są uzależnione od czasu przebiegu w tablicy, jak List.toArray()
i Arrays.copyOf()
itp
2) Różnica Wspomniałem wyżej, że jest zbyt skomplikowany, aby można go było łatwo odróżnić automatycznie.
Czy widziałeś przykład (i wyjaśnienie) w [JavaDoc] (http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html)? – jlordo
Na trzecie pytanie, jedną z praktyk jest zawsze mieć pierwszy element i inne: 'retType myMethod (Arg pierwszy, Arg ... inne)'. Jeśli nie określisz 'first', dozwolona jest pusta tablica i możesz bardzo dobrze mieć metodę o tej samej nazwie z tym samym typem zwracanym bez przyjmowania argumentów, co oznacza, że JVM będzie miała trudności z określeniem, która metoda powinna być nazywa. – fge
@jlordo Zrobiłem, ale nie rozumiem, dlaczego jest podany w kontekście varargs, jak można łatwo replikować tę sytuację poza funkcją varargs (zweryfikowano to, kompiluje z oczekiwanymi ostrzeżeniami bezpieczeństwa i błędami w czasie wykonywania) .. – Oren