2012-12-11 15 views
19

Eventuali duplicati:
Double brace initialisation (anonymous inner class) with diamond operatorPerché non è possibile inferire tipi di diamante su classi interne anonime?

In Java 7 e versioni successive, il diamante può essere utilizzato per inferire i tipi avanti normalmente in questo modo, senza un problema:

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

Tuttavia, non può per classi interne anonime come questa:

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

    //Implementation here 

} 

Perché è questo? Logicamente in questo scenario, posso sicuramente dedurre il tipo come String. C'è una ragione logica per questa decisione in cui il tipo non può in realtà essere dedotto su classi interne anonime, o è stato omesso per altri motivi?

+2

@Philipp Non sono d'accordo - quella domanda è chiedere perché un certo pezzo di codice non viene compilato (anzi la risposta è solo che non è possibile utilizzare il diamante con classi interne anonime), questo sta chiedendo il motivo tecnico/logico per * perché * gli sviluppatori Java hanno scelto di mettere in atto quella particolare restrizione. Correlati, ma difficilmente la stessa cosa. – berry120

+1

Questo è stato notevolmente migliorato in JDK 9: https://bugs.openjdk.java.net/browse/JDK-8062373 –

risposta

12

Nella JSR-334:

Utilizzando diamante con le classi interne anonime non è supportato dal farlo in generale richiederebbe estensioni file di classe attributo firma per rappresentare tipi non denotable, un de facto JVM cambiamento.

Ciò che immagino sia che, come tutti sanno, la classe anonima conduce a una generazione del proprio file di classe.

Immagino che il tipo generico non esista all'interno di questi file e piuttosto sostituito dal tipo effettivo (statico) (così dichiarato dal tipo esplicito come <String> all'ora dell'oggetto dichiarazione).

In effetti, il file corrispondente a una classe interna non viene mai condiviso tra più istanze diverse di esso, quindi perché preoccuparsi di generici? :).

Sarebbe più difficilmente realizzabile (e sicuramente inutile) per il compilatore forzare un'estensione (aggiungendo un attributo speciale per generici) a questi tipi di file di classe.

1

In breve, lo <> fa poco per dedurre i tipi, si spegne l'avviso che si otterrebbe senza di esso.

MODIFICA: come @Natix indica che esegue un controllo.

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

produce un errore di compilazione

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

Come si può vedere la <> sta prendendo il tipo dell'argomento, non inferire il tipo dal tipo di copy

+3

Non esattamente, il diamante esegue ancora un controllo del tipo. Potrebbe non essere ovvio per le semplici inizializzazioni della raccolta, ma prendiamo ad esempio un costruttore di copia: 'Lista copy = new ArrayList <> (originale);' Questo assicura che l'elenco 'original' sia anche un elenco di stringhe (o più precisamente una 'Collection '). – Natix

+0

Mentre questo è vero, non sta inferendo il tipo da come viene utilizzato. Sta prendendo il tipo da una discussione. –

4

google rendimenti, dopo aver saltato i messaggi da StackOverflow, http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html

Sto indovinando è come questo, di solito una classe anonima è una sottoclasse concreta del tipo apparente

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

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

è implementato da

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

    Foo<Integer> foo = new AnonFoo_1(); 

Supponiamo di consentire l'inferenza del diamante su classi anonime, ci può essere un caso complicato come

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

Le regole di inferenza producono N=Number&Runnable; dopo il trucco di implementazione precedente, è necessario

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

Attualmente non consentito; il tipo arg a super type Foo deve essere di tipo "normale".


Tuttavia, la logica non è molto forte. Possiamo inventare altri trucchi di implementazione per farlo funzionare

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<>(); 

il compilatore dovrebbe essere in grado di fare lo stesso trucco.

In ogni caso, almeno il compilatore dovrebbe consentire la maggior parte dei casi d'uso che non coinvolgono "tipi indistinguibili", come Foo<Integer> foo = new Foo<>(){...} È un peccato che questi casi comuni/semplici siano anch'essi inutilmente proibiti.

+0

Il tuo esempio è strettamente applicabile con una semplice classe concreta (non anonima). Qual è la caratteristica specifica corrispondente alla classe anonima nel tuo campione? Infatti, se la tua teoria fosse vera, mai diamante potrebbe essere applicabile in ogni caso. – Mik378

+0

a livello di codice byte, non penso che ci sia qualcosa di speciale nelle classi anonime. – irreputable

+0

Così il campione si sarebbero comportati esattamente nello stesso modo, se invece di avere un'interfaccia 'foo' avete: class Foo { void foo (N n) {}} Foo foo = new Foo (); – Mik378

Problemi correlati