1) Es gibt viele Beispiele im Internet und in StackOverflow zu dem speziellen Problem mit Generika und Varargs. Grundsätzlich ist es, wenn Sie eine variable Anzahl von Argumenten eines Typ-Parameter-Typs haben:
<T> void foo(T... args);
In Java sind Varargs ein syntaktischer Zucker, der zur Kompilierungszeit einfach "neu geschrieben" wird: Ein Varargs-Parameter vom Typ X...
wird in einen Parameter vom Typ konvertiert X[]
. und jedes Mal, wenn diese varargs-Methode aufgerufen wird, sammelt der Compiler alle "variablen Argumente", die im varargs-Parameter enthalten sind, und erstellt ein Array wie folgt new X[] { ...(arguments go here)... }
.
Dies funktioniert gut, wenn der Vararg-Typ konkret ist String...
. Wenn es sich um eine Typvariable handelt T...
, funktioniert dies auch, wenn T
bekannt ist, dass es sich um einen konkreten Typ für diesen Aufruf handelt. Wenn die oben beschriebene Methode Teil einer Klasse wäre Foo<T>
und Sie eine Foo<String>
Referenz haben, foo
wäre es in Ordnung , sie aufzurufen , da wir wissen, dass sie T
sich String
an diesem Punkt im Code befindet.
Es funktioniert jedoch nicht, wenn der "Wert" von T
ein anderer Typparameter ist. In Java ist es unmöglich, ein Array eines Typparameter-Komponententyps ( new T[] { ... }
) zu erstellen . Also verwendet Java stattdessen new Object[] { ... }
(hier Object
ist die Obergrenze von T
; wenn die Obergrenze etwas anderes wäre, wäre es das anstelle von Object
) und gibt Ihnen dann eine Compiler-Warnung.
Was ist also falsch daran, new Object[]
statt new T[]
oder was auch immer zu kreieren ? Nun, Arrays in Java kennen ihren Komponententyp zur Laufzeit. Daher hat das übergebene Array-Objekt zur Laufzeit den falschen Komponententyp.
Für die wahrscheinlich häufigste Verwendung von varargs, um einfach über die Elemente zu iterieren, ist dies kein Problem (der Laufzeittyp des Arrays ist Ihnen egal), daher ist dies sicher:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Für alles, was vom Laufzeitkomponententyp des übergebenen Arrays abhängt, ist es jedoch nicht sicher. Hier ist ein einfaches Beispiel für etwas, das unsicher ist und abstürzt:
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");
}
}
Das Problem hierbei ist , dass wir von der Art der abhängig args
zu sein T[]
, um sie als zurückzukehren T[]
. Tatsächlich ist der Typ des Arguments zur Laufzeit jedoch keine Instanz von T[]
.
3) Wenn Ihre Methode ein Argument vom Typ hat T...
(wobei T ein beliebiger Typparameter ist), dann:
- Sicher: Wenn Ihre Methode nur von der Tatsache abhängt, dass die Elemente des Arrays Instanzen von sind
T
- Unsicher: Wenn es davon abhängt, dass das Array eine Instanz von ist
T[]
Dinge , die auf dem Laufzeittyp des Arrays abhängig sind: es als Typ - Rückkehr T[]
, es als Argument für einen Parameter vom Typ vorbei T[]
, immer den Array - Typ verwendet .getClass()
, überschreiten sie sich auf Verfahren , die auf dem Laufzeittyp des Arrays abhängen, wie List.toArray()
und Arrays.copyOf()
, etc.
2) Die oben erwähnte Unterscheidung ist zu kompliziert, um automatisch unterschieden zu werden.