it-swarm.com.de

Mögliche Haufenverschmutzung über varargs-Parameter

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?

377
hertzsprung

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" ClassCastExceptions 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.

219
Ben Schulz

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:

220
Gili

@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.

7
jontro

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.

5
Peter Lawrey

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.

0
user1122069