2013-07-17 12 views
6

Questo codice causa un errore di compilazione con javac (ma, in particolare, non con Eclipse 4.2.2!):Perché non è consentita la conversione in "GenericType <?>" qui?

public interface Foo<T> { 
} 

class Bar<T> implements Foo<Iterable<T>> { 
} 

class Test { 
    void test(Foo<? extends Iterable<? extends String>> foo) { 
     Bar<?> bar = (Bar<?>) foo; 
    } 
} 

L'errore da javac è questo:

Foo.java:9: error: inconvertible types 
     Bar<?> bar = (Bar<?>) foo; 
          ^
    required: Bar<?> 
    found: Foo<CAP#1> 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Iterable<? extends String> from capture of ? extends Iterable<? extends String> 

Cambiare il cast a (Bar) foo (cioè utilizzando il tipo non elaborato) consente la compilazione del codice, così come la modifica del tipo di foo in Foo<? extends Iterable<?>>.

EDIT: Hilariously, questa semplice modifica fa sì che di Eclipse a rifiutare, ma javac per accettare:

void test(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

E, sia Eclipse e javac rifiutano questo:

void test(Foo<Iterable<? extends String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 
+0

Speriamo che tu non debba mai fare un brutto casting così e questa è solo una domanda di curiosità ... – jahroy

+0

Sì, solo curiosità. L'esempio di vita reale era in realtà un'istanza di controllo in cui "instanceof Bar" funzionava ma "instanceof Bar " no. –

+0

C'è qualche menzione qui, http://docs.oracle.com/javase/tutorial/java/generics/capture.html –

risposta

2

Se un programmatore desidera eseguire il cast esplicito di un tipo X per digitare Y, il linguaggio potrebbe semplicemente consentirlo, assumendo che il programmatore conosca meglio del compilatore.

Ma i bravi ragazzi di Java vogliono evitare alcuni cast ovviamente impossibili, ad es. (Cat)dog. Quindi hanno questa sezione dettagliata gloriosa dedicata al tema - http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

Ma, ci sono problemi con queste regole? E i compilatori sono conformi a queste regole? ... Queste domande sono troppo complesse e non molto interessanti.

Quello che dovremmo preoccuparci è se il cast ha senso; se ha senso, e il compilatore si rifiuta di accettarlo, nessun problema, basta aggirarlo.


Nella tua domanda, ci sono due posti per inserire i caratteri jolly, in Foo< >, e in Iterable< >. In ogni luogo, il carattere jolly può essere

0. None 
    1. Bounded "? extends Something" 
    2. Unbounded "?" 

quindi cerchiamo di esplorare tutte le combinazioni, ed ecco la mia conclusione:

 wildcard#1 wildcard#2  should_compile javac  eclipse 

00 -    -    Y    Y   N 
01 -    ? extends  N    N   N 
02 -    ?    N    N   N 
10 ? extends  -    Y    N   Y 
11 ? extends  ? extends  Y    N   Y 
12 ? extends  ?    Y    Y   Y 
20 ?    -    Y    Y   Y 

should_compile significa il cast ha un senso o no, ha spiegato in seguito.

Nel caso 10 and 11, il codice deve essere compilato, ma javac lo rifiuta. O le regole hanno problemi, o javac ha dei bug.


Vediamo, ad esempio, il motivo per caso 00 ha senso e dovrebbe compilare

void test00(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

the question is, could there be a class/interface `X`, such that 
     Bar<X> <: Foo<Iterable<String>> 
=> Foo<Iterable<X>> <: Foo<Iterable<String>> 
=> Iterable<X> = Iterable<String> 
=> X = String 
so the answer is yes, the cast makes sense. 

e perché caso 01 non dovrebbe compilare

 Foo<Iterable<X>> <: Foo<Iterable<? extends String>> 
=> Iterable<X> = Iterable<? extends String> 
=> NO SOLUTION 
    note that Iterable<String> != Iterable<? extends String> 

e caso 11

 Foo<Iterable<X>> <: Foo<? extends Iterable<? extends String>> 
=> Iterable<X> <: Iterable<? extends String> 
=> X <: String 

È sorprendente che il caso 01 non debba essere compilato, sebbene sia sensato. Il problema principale è, Iterable è convocante, e dovremmo usare un carattere jolly con esso quasi ovunque sia usato. Quindi idealmente dovremmo dichiarare

class Bar<T> implements Foo<Iterable<? extends T>> 

ma la vita è un inferno se inseriamo caratteri jolly ovunque. La soluzione migliore è la variazione del sito di dichiarazione. Non sono sicuro che Java aggiungerà mai questa funzionalità prima che tutti andiamo in pensione.

+0

Sono sorpreso dal caso 01. In effetti sono sorpreso che questo non sia valido: "Foo > foo = new Foo >(); " –

+0

Ohhhh wow, okay, non mi sono reso conto che Foo > era un tipo completo. –

+0

@TavianBarnes Sì, i caratteri jolly nidificati sono molto diversi dai caratteri jolly di livello superiore: http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic-methods-makes-java-compiler-and-me- very-confu/3547372 # 3547372 –

4

Il codice è ragionevole e compilare: si sta restringendo il tipo statico Foo a Bar mentre si allarga il tipo generico ? extends Iterable<? extends String> a ?, in cui il livello superiore ? acquisisce caratteri jolly. È un cast controllato e dovrebbe compilare senza preavviso.

È necessario submit an Oracle bug. Ho provato a cercare una segnalazione di errore corrispondente, ma il più vicino possibile a questo problema è stato this old closed ticket (IMO il database di bug di Oracle è molto difficile da cercare). Cercherò di più quando ne avrò la possibilità, perché questo problema di javac sembra molto familiare.

Problemi correlati