Ich verstehe dies unter Java 7, wenn Varargs mit einem generischen Typ verwendet werden.
Aber meine Frage ist ..
Was genau bedeutet Eclipse, wenn es sagt "seine Verwendung könnte den Haufen möglicherweise verschmutzen?"
Und
Wie verhindert die neue @SafeVarargs
-Annotation dies?
Haufenverschmutzung ist ein Fachbegriff. Es bezieht sich auf Referenzen, die einen Typ haben, der kein Supertyp des Objekts ist, auf das sie zeigen.
List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As
Dies kann zu "unerklärlichen" ClassCastException
s führen.
// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0);
@SafeVarargs
verhindert dies überhaupt nicht. Es gibt jedoch Methoden, die den Heap nachweislich nicht verschmutzen, der Compiler kann es einfach nicht beweisen. Bisher hatten Anrufer solcher APIs ärgerliche Warnungen, die völlig sinnlos waren, aber an jeder Anrufstelle unterdrückt werden mussten. Jetzt kann der API-Autor dies einmal auf der Deklarationsseite unterdrücken.
Wenn die Methode tatsächlich nicht safe ist, werden Benutzer jedoch nicht mehr gewarnt.
Wenn du erklärst
public static <T> void foo(List<T>... bar)
Der Compiler konvertiert es in
public static <T> void foo(List<T>[] bar)
dann zu
public static void foo(List[] bar)
Es besteht dann die Gefahr, dass Sie versehentlich falsche Werte in die Liste eintragen und der Compiler keinen Fehler auslöst. Wenn beispielsweise T
ein String
ist, wird der folgende Code fehlerfrei kompiliert, schlägt jedoch zur Laufzeit fehl:
// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;
// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));
// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);
Wenn Sie die Methode überprüft haben, um sicherzustellen, dass sie keine derartigen Sicherheitsanfälligkeiten enthält, können Sie sie mit @SafeVarargs
kommentieren, um die Warnung zu unterdrücken. Verwenden Sie für Schnittstellen @SuppressWarnings("unchecked")
.
Wenn Sie diese Fehlermeldung erhalten:
Die Varargs-Methode kann eine Haufenverschmutzung durch nicht nachweisbare Varargs-Parameter verursachen
und Sie sind sicher, dass Ihre Verwendung sicher ist, dann sollten Sie stattdessen @SuppressWarnings("varargs")
verwenden. Siehe Ist @SafeVarargs eine geeignete Anmerkung für diese Methode? und https://stackoverflow.com/a/14252221/14731 für eine gute Erklärung dieser zweiten Art von Fehler.
Verweise:
@SafeVarargs
verhindert zwar nicht, dass der Compiler jedoch strenger ist, wenn er Code kompiliert, der ihn verwendet.
http://docs.Oracle.com/javase/7/docs/api/Java/lang/SafeVarargs.html erläutert dies ausführlicher.
Heap-Verschmutzung liegt vor, wenn Sie eine ClassCastException
erhalten, wenn Sie eine Operation an einer generischen Schnittstelle ausführen und diese einen anderen als den deklarierten Typ enthält.
Wenn Sie varargs verwenden, kann ein Object[]
erstellt werden, der die Argumente enthält.
Aufgrund der Escape-Analyse kann das JIT diese Array-Erstellung optimieren. (Eines der wenigen Male, bei denen ich herausfand, dass dies der Fall ist.) Es wird nicht garantiert, dass es wegoptimiert wird, aber ich würde mir keine Sorgen darüber machen, es sei denn, Sie sehen ein Problem in Ihrem Speicherprofilersteller.
AFAIK @SafeVarargs
unterdrückt eine Warnung des Compilers und ändert nicht das Verhalten der JIT.
Der Grund liegt darin, dass varargs die Option bietet, mit einem nicht parametrisierten Objektarray aufgerufen zu werden. Wenn Ihr Typ List <A> ... war, kann er auch mit dem Typ List [] non-varargs aufgerufen werden.
Hier ist ein Beispiel:
public static void testCode(){
List[] b = new List[1];
test(b);
}
@SafeVarargs
public static void test(List<A>... a){
}
Wie Sie sehen, kann List [] b jede Art von Consumer enthalten, und dennoch wird dieser Code kompiliert. Wenn Sie varargs verwenden, sind Sie in Ordnung. Wenn Sie jedoch die Methodendefinition nach type-erasure - void test (List []) - verwenden, überprüft der Compiler die Typen der Vorlagenparameter nicht. @SafeVarargs unterdrückt diese Warnung.