Ich bin kürzlich auf die Java- @SafeVarargs
Annotation gestoßen . Das Googeln nach dem, was eine variable Funktion in Java unsicher macht, hat mich ziemlich verwirrt (Haufenvergiftung? Gelöschte Typen?), Daher möchte ich ein paar Dinge wissen:
Was macht eine variadische Java-Funktion in dem
@SafeVarargs
Sinne unsicher (vorzugsweise in Form eines ausführlichen Beispiels erklärt)?Warum liegt diese Anmerkung im Ermessen des Programmierers? Sollte der Compiler dies nicht überprüfen können?
Gibt es einen Standard, den man einhalten muss, um sicherzustellen, dass seine Funktion tatsächlich varags-sicher ist? Wenn nicht, welche Best Practices sind erforderlich, um dies sicherzustellen?
retType myMethod(Arg first, Arg... others)
. Wenn Sie nichts angebenfirst
, ist ein leeres Array zulässig, und Sie haben möglicherweise eine Methode mit demselben Namen und demselben Rückgabetyp, die keine Argumente akzeptiert. Dies bedeutet, dass die JVM Schwierigkeiten hat, zu bestimmen, welche Methode aufgerufen werden soll.Antworten:
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:
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 konvertiertX[]
. 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 folgtnew X[] { ...(arguments go here)... }
.Dies funktioniert gut, wenn der Vararg-Typ konkret ist
String...
. Wenn es sich um eine Typvariable handeltT...
, funktioniert dies auch, wennT
bekannt ist, dass es sich um einen konkreten Typ für diesen Aufruf handelt. Wenn die oben beschriebene Methode Teil einer Klasse wäreFoo<T>
und Sie eineFoo<String>
Referenz haben,foo
wäre es in Ordnung , sie aufzurufen , da wir wissen, dass sieT
sichString
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 stattdessennew Object[] { ... }
(hierObject
ist die Obergrenze vonT
; wenn die Obergrenze etwas anderes wäre, wäre es das anstelle vonObject
) und gibt Ihnen dann eine Compiler-Warnung.Was ist also falsch daran,
new Object[]
stattnew 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:
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:
Das Problem hierbei ist , dass wir von der Art der abhängig
args
zu seinT[]
, um sie als zurückzukehrenT[]
. Tatsächlich ist der Typ des Arguments zur Laufzeit jedoch keine Instanz vonT[]
.3) Wenn Ihre Methode ein Argument vom Typ hat
T...
(wobei T ein beliebiger Typparameter ist), dann:T
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 vorbeiT[]
, immer den Array - Typ verwendet.getClass()
, überschreiten sie sich auf Verfahren , die auf dem Laufzeittyp des Arrays abhängen, wieList.toArray()
undArrays.copyOf()
, etc.2) Die oben erwähnte Unterscheidung ist zu kompliziert, um automatisch unterschieden zu werden.
quelle
FOO<T extends Something>
Wäre es sicher, das T [] einer varargs-Methode als Array vonSomthing
s zurückzugeben?@SafeVarargs
ist, darin besteht, dass Sie das Array nur an eine andere Methode übergeben, die bereits so kommentiert ist (zum Beispiel: Ich schreibe häufig vararg-Methoden, die existieren, um ihr Argument mit einerArrays.asList(...)
anderen Methode in eine Liste umzuwandeln und an eine andere Methode zu übergeben (ein solcher Fall kann immer mit Anmerkungen versehen werden,@SafeVarargs
daArrays.asList
die Anmerkung vorhanden ist).@SafeVarargs
sei denn, die Methode iststatic
oderfinal
, weil es sonst in einer abgeleiteten Klasse mit etwas Unsicherem überschrieben werden könnte.private
Methoden zulässig , die ebenfalls nicht überschrieben werden können.Berücksichtigen Sie dies für Best Practices.
Wenn Sie dies haben:
Ändern Sie es in dieses:
Ich habe festgestellt, dass ich normalerweise nur Varargs hinzufüge, um es für meine Anrufer bequemer zu machen. Für meine interne Implementierung wäre es fast immer bequemer, a zu verwenden
List<>
.Arrays.asList()
Um dies zu verhindern und sicherzustellen, dass ich Heap Pollution nicht einführen kann, ist dies das, was ich tue.Ich weiß, dass dies nur Ihre Nummer 3 beantwortet. newacct hat eine großartige Antwort für # 1 und # 2 oben gegeben, und ich habe nicht genug Ruf, um dies nur als Kommentar zu hinterlassen. : P.
quelle