2011-08-28 15 views
13

Se ho una classe Foo:inferenza generico nei costruttori

public class Foo<T> { 
    public Foo(T t) { 
     //do something 
    } 

    public static <E> void bar(E e) { 
     //do something 
    } 
} 

Perché Foo.bar("String"); dedurre che E è una stringa (e quindi non lanciare un avviso del compilatore), ma new Foo("String"); non dedurre che T è una stringa?

+1

Qual è l'errore preciso che stai vedendo? – linuxuser27

+0

@ linuxuser27 Viene visualizzato un avviso di tipo non elaborato su 'new Foo (" String ");' – Jeffrey

risposta

10

poiché il costruttore può essere considerato un metodo di istanza speciale, non è tipizzato - prende il tipo dal nome della classe (con un parametro di tipo), per esempio Foo<String>. cioè il costruttore non è definito come:

public <T> Foo(T t) ... 

né può essere. In questo modo sarebbe nascondere il tipo generico della classe (e si otterrà un messaggio di avviso)

Il metodo statico invece è digitato. Cordiali saluti, la chiamata generica-parametro-less, una volta che il tipo è dedotto, è equivalente a:

Foo.<String>bar("String"); 
+1

+1. Molto interessante, ma non vedo ancora perché in questo caso non può essere dedotto da un meccanismo simile dall'elenco degli argomenti – linuxuser27

+0

Avete notato questo: Se dichiarate la vostra classe in questo modo: 'classe TestClass ' e il vostro costruttore 'public TestClass (T t)' non si ottiene un avviso, ma se si esegue questa operazione: 'classe TestClass ' e il costruttore 'pubblico TestClass (T t)' viene visualizzato un avvertimento del compilatore che indica "Il parametro tipo T sta nascondendo il tipo T " –

+0

@Varun Achar È possibile dichiarare [generici costruttori] (http://download.oracle.com/javase/tutorial/java/generics/genmethods.html) e se si dichiara nuovamente su un costruttore stai effettivamente nascondendo la classe '. Credo che boehemian stia cercando di dire che siccome non sto creando un costruttore generico, esso eredita il suo tipo dalla classe, non viceversa. Ma lascia ancora una domanda sul perché la classe non possa dedurre il parametro type da un costruttore. – Jeffrey

1

penso che avrete bisogno di fare questo

new Foo<String>("String"); 

a dire ottenere informazioni generici passato; simile all'API delle collezioni.

+2

Che è corretto. L'OP non sta chiedendo 'cosa' deve essere fatto ma 'perché', poiché nel fatto di una chiamata al metodo statico questo non è necessario. – linuxuser27

+0

Che cosa ha detto linuxuser, facendo 'new Foo (" String ");' sembra ripetitivo quando stai già passando 'String' al costruttore. – Jeffrey

+0

Penso che sarà meno ripetitivo in Java 7. –

1

Guardando in questo, ho intenzione di prendere una congettura qui. Si consideri il seguente:

public class Foo { 
    public <E> Foo(E t) { 
     //do something 
    } 

    public static <E> void bar(E e) { 
     //do something 
    } 
} 

Nella classe di cui sopra si ottiene alcun avviso quando si crea un'istanza Foo come segue:

Foo f = new Foo("String"); 

Questo funziona perché il tipo di E viene dedotta qui. Proprio come ti aspetti che accada nel caso del metodo. Tuttavia, l'errore che si ottiene non è perché il tipo di argomento non viene dedotto, ma perché il tipo non elaborato per la classe non può essere dedotto.

Penso che ciò che ne consegue è che il tipo raw Class può essere propagato ai metodi, ma i metodi non possono impostare il tipo raw di classe usando inferenza.

+0

Credo che questo sia ciò che stava arrivando in Boemia. Poiché non si dichiara il parametro di tipo della classe, al costruttore non viene assegnato un tipo. – Jeffrey

+0

@Jeffrey - Sono d'accordo.Questa è stata una grande domanda. Grazie. – linuxuser27

3

Quando Java ha implementato i generici, è stato deciso che una classe generica istanziata senza parametri di tipo restituiva sempre un tipo non elaborato. Questo differisce dai metodi generici che mancano i parametri di tipo, che il compilatore tenta di inferire il tipo di. Dalle esercitazioni Java:

In genere, il compilatore Java può dedurre i parametri di tipo di una chiamata di metodo generica. Di conseguenza, nella maggior parte dei casi, non è necessario specificarli.

Ma quando la discussione si rivolge a costruttori:

Si noti che per approfittare di inferenza di tipo automatico durante istanza di classe generica, è necessario specificare il diamante.Nel seguente esempio, il compilatore genera un allarme conversione incontrollato poiché il costruttore HashMap() si riferisce al tipo grezzo HashMap, non il tipo Map<String, List<String>>:

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

fonte: http://download.oracle.com/javase/tutorial/java/generics/gentypeinference.html

Questo rimane lo stesso in Java 7, tuttavia hanno cercato di renderlo meno ripetitivo supportando la sintassi dei diamanti. Ad esempio, Foo<String> foo = new Foo<>("String"). Vedi this section of the same article.

+0

Buona documentazione. – Jeffrey

1

@Kublai La risposta di Khan è corretta; il tipo di new Foo(s) è raw Foo, per compatibilità con le versioni precedenti.

L'inferenza del tipo di diamante del costruttore Java7 (new Foo<>(s)) è la stessa del metodo e definita in termini di inferenza del metodo.

http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html#diamond

Se l'espressione creazione dell'istanza classe utilizza "<>" per elidere argomenti di tipo di classe, un elenco dei metodi m1 ... mk è definito ai fini della risoluzione di sovraccarico e l'inferenza tipo di argomento. ..

... poi uno dei m1 ... mk viene selezionato, utilizzando il processo descritto in §15.12.2 (Determinare firma del metodo)

tuo sospetto è corretto, costruttori c un'inferenza all'uso proprio come i metodi, non ci sono differenze essenziali. Devi solo aggiungere <> a causa di problemi di compatibilità con le versioni precedenti.

Perché Java ha impiegato 6 anni per aggiungere questa funzionalità è un'altra storia.

Problemi correlati