2012-12-11 8 views
19

Mögliche Duplizieren:
Double brace initialisation (anonymous inner class) with diamond operatorWarum kann Diamond nicht auf anonyme innere Klassen schließen?

In Java 7 und höher können Diamanttypen verwendet werden auf normalerweise ohne ein Problem wie so zu folgern:

List<String> list = new ArrayList<>(); 

jedoch Es kann nicht für anonyme innere Klassen wie folgt sein:

List<String> st = new List<>() { //Doesn't compile 

    //Implementation here 

} 

Warum ist das? Logischerweise kann ich in diesem Szenario den Typ eindeutig als String ableiten. Gibt es einen logischen Grund für diese Entscheidung, wenn der Typ nicht auf anonyme innere Klassen geschlossen werden kann oder aus anderen Gründen weggelassen wurde?

+2

@Philipp Ich stimme nicht zu - diese Frage fragt, warum ein bestimmter Code nicht kompiliert (in der Tat ist die Antwort nur, dass Sie Diamant mit anonymen inneren Klassen nicht verwenden können), dieser fragt den technisch/logischen Grund für * warum * die Java-Entwickler entschieden haben, diese bestimmte Einschränkung an Ort und Stelle zu setzen. Verwandt, aber kaum das Gleiche. – berry120

+1

Dies wurde in JDK 9 erheblich verbessert: https://bugs.openjdk.java.net/browse/JDK-8062373 –

Antwort

12

Im JSR-334: Dabei würde im allgemeinen erfordern Erweiterungen der Klassendatei Signaturattribut zu repräsentieren nicht-denotable Typen

mit anonymen inneren Klassen wird mit Diamant nicht seit unterstützt, eine de facto JVM ändern.

Was ich denke ist, dass, wie jeder weiß, anonyme Klasse zu einer Generation ihrer eigenen Klassendatei führt.

Ich stelle mir vor, dass generischer Typ nicht innerhalb dieser Dateien existiert und eher durch den effektiven (statischen) Typ ersetzt wird (also durch den expliziten Typ wie <String> zur Deklarationsobjektzeit deklariert).

In der Tat wird die Datei, die einer inneren Klasse entspricht, nie über mehrere verschiedene Instanzen geteilt, also warum sollte man sich mit Generika beschäftigen ?! :).

Es wäre schwieriger (und sicherlich nutzlos) für den Compiler, eine Erweiterung (durch Hinzufügen eines speziellen Attributs für Generika) zu dieser Art von Klassendateien zu erzwingen.

1

Kurz gesagt, die <> macht wenig, um Typen zu schließen, es schaltet die Warnung ab, die Sie ohne es bekommen würden.

EDIT: Wie @Natix weist darauf hin, dass es einige Überprüfung.

List<Integer> ints = new ArrayList<>(); 
List<String> copy = new ArrayList<>(ints); 

erzeugt ein Übersetzungsfehler

Error:Error:line (42)error: incompatible types 
required: List<String> 
found: ArrayList<Integer> 

Wie Sie die <> nimmt den Typ des Arguments sehen können, nicht die Art von der Art der copy

+3

Nicht genau, Diamant führt immer noch eine Typprüfung durch. Es mag für einfache Auflistungsinitialisierungen nicht offensichtlich sein, aber nehmen Sie zum Beispiel einen Kopierkonstruktor: 'List copy = new ArrayList <> (original);' Dies stellt sicher, dass die 'original'-Liste auch eine Liste von Strings (oder mehr) ist genau eine 'Collection '). – Natix

+0

Während dies zutrifft, leitet es den Typ nicht von seiner Verwendung ab. Es nimmt den Typ von einem Argument. –

4

google liefert nach Posts von Stackoverflow, http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html

Ich vermute, es ist so, in der Regel eine anonyme Klasse ist eine konkrete Unterklasse des scheinbaren Typs

interface Foo<N extends Number> 
    { 
     void foo(N n); 
    } 

    Foo<Integer> foo = new Foo<Integer>(){ ... } 

implementiert wird durch

Skipping
class AnonFoo_1 implements Foo<Integer>{ ... } 

    Foo<Integer> foo = new AnonFoo_1(); 

Angenommen, wir erlauben Diamant-Rückschluss auf anonyme Klassen, kann es komplizierter Fall wie

01 sein
Foo<? extends Runnable> foo = new Foo<>(){ ... } 

Die Inferenzregeln ergeben N=Number&Runnable; Nach dem vorherigen Implementierungstrick benötigen wir

class AnonFoo_2 implements Foo<Number&Runnable>{ ... } 

Das ist derzeit nicht erlaubt; Der Typ arg to super type Foo muss ein "normaler" Typ sein.


Allerdings ist die Begründung nicht sehr stark. Wir können andere Implementierungstricks erfinden, um es zum Laufen zu bringen

class AnonFoo<N extends Number&Runnable> implements Foo<N> 
    { 
     @Override public void foo(N n) 
     { 
      n.intValue(); 
      n.run(); 
     } 
    } 

    Foo<? extends Runnable> foo = new AnonFoo<>(); 

der Compiler sollte in der Lage sein, den gleichen Trick zu tun.

In jedem Fall sollte zumindest der Compiler die Mehrheit der Anwendungsfälle erlauben, die keine "unbenotable Typen" beinhalten, wie Foo<Integer> foo = new Foo<>(){...}. Es ist schade, dass diese einfachen Fälle auch unnötig verboten sind.

+0

Ihr Beispiel ist strikt anwendbar mit einer einfachen (nicht anonymen) konkreten Klasse. Was ist das spezifische Merkmal, das der anonymen Klasse in Ihrer Stichprobe entspricht? In der Tat, wenn Ihre Theorie wahr wäre, könnte Diamant niemals anwendbar sein, egal wie der Fall ist. – Mik378

+0

auf Bytecode-Ebene, ich glaube nicht, dass anonyme Klassen etwas Besonderes sind. – irreputable

+0

So Ihre Probe auf genau die gleiche Art und Weise 'wenn statt mit einer Schnittstelle verhalten würde Foo' Sie haben: class Foo { void foo (N n) {}} Foo foo = new Foo (); – Mik378

Verwandte Themen