2013-06-30 15 views
11

Come si può ottenere una funzionalità simile senza errori?Forzare che i parametri generici Java siano dello stesso tipo

class A<K> { 
    void f(K x) {} 
} 

void foo(A<? extends X> a, X x) { 
    a.f(x); // AN error: The method f(capture#1-of ? extends X) in the 
      // type A<capture#1-of ? extends X> is not applicable for the 
      // arguments (X) 
} 

so che accade perché 'a' può essere un'istanza di un < "non-X">, per cui il suo 'f' non deve accettare un'istanza di X come parametro, ma come può Io impongo che i parametri siano dello stesso tipo?

qui è più codice:

classe Test:

class Test { 
    <T> void foo(A<T> a, T x) { 
    a.f(x); // now it works! 
} 
} 

In qualche classe:

Container<X> container; 
public void test() { 
    X x = new X(); 
    new Test().foo(container.get(), x); 
} 

Ecco la classe contenitore:

public class Container<K> { 
    A<? extends K> get() { 
    return new A<K>(); 
    } 
} 
+0

X è un parametro di classe o di tipo? – Joni

risposta

17

È possibile forza il paramet ERS essere dello stesso tipo effettuando le seguenti operazioni:

// the first class, A<K>: 
class A<K> { 
    void f(K x) {} 
} 

// the second class, defining the method with generic type parameters 
class Test { 
    <T> void foo(A<T> a, T x) { 
    a.f(x); // now it works! 
    } 
} 

// a third class, that uses the above two: 
class Main { 
    public static void main(final String... args) { 
    final Test test = new Test(); 
    final A<String> a = new A<>(); 
    test.foo(a, "bar"); 
    } 
} 

Quello che fa è: il metodo foo definisce un parametro di tipo generico T e lo usa per far rispettare che il parametro K tipo della classe A deve corrispondere il tipo di x, il secondo parametro di foo.

Si potrebbe anche imporre restrizioni su <T> se lo si desidera e se ha senso per il proprio problema, ad esempio <T extends Bar> void foo(A<T> a, T x) {...} o con super. Lo si vorrebbe se, come Joni chiesto su un commento nella domanda, X è in realtà un tipo e non un parametro di tipo: si utilizzerà <T extends X> void foo(...).


Dopo aver mostrato più codice, il problema diventa chiaro.

Il metodo .get() del contenitore restituisce un'istanza di A<? extends K>. Pertanto, il parametro type dell'istanza ottenuta da .get() non è stato specificato completamente. Di solito, non è un ottimo progetto restituire un tipo così non specificato. Per una presentazione video con Joshua Bloch, l'autore di Effective Java e molte API e funzionalità in Java, che mostrano come migliorare tale API, controllare questo: http://www.youtube.com/watch?v=V1vQf4qyMXg&feature=youtu.be&t=22m. A esattamente 25'36 ", Joshua Bloch dice" non tentare di usarli [tipi di caratteri jolly] sui valori di ritorno ", e lo spiega in un secondo momento, in pratica non si ottiene più flessibilità nell'usarli e basta dolorosamente difficile per gli utenti dell'API per farvi fronte (avete appena sentito gli effetti di farlo ...)

Per risolvere, si potrebbe semplicemente provare a cambiare la firma di .get() a A<K> get(), quindi la classe contenitore sarebbe:

public class Container<K> { 
    A<K> get() { 
    return new A<K>(); 
    } 
} 

Dal momento che non sa che sta tornando get() un'istanza di A<K>, non c'è ragione per usare la firma più vecchio: si fa semplicemente perdere le informazioni che già conosci !

E se ancora non funziona, il problema potrebbe essere da qualche altra parte, e avresti bisogno di mostrare ancora più codice ... o meglio ancora, fai altre domande!:)

+0

Ma come può essere usato? 'new Test(). Foo (a, x);' restituisce ancora un errore: Il metodo foo (A , T) nel tipo A . Test non applicabile per gli argomenti (A , X) – Evgeny

+0

Sembra che tu stia definendo la classe 'Test' come una classe interna di' A', che non è esattamente ciò che ho mostrato sopra. –

+0

Non sembra importare: l'errore ora è 'Il metodo foo (A , T) nel tipo Test non è applicabile per gli argomenti (A , X)' – Evgeny

6

tenendo presente il PECS rule, e dato il modo in cui si utilizza X, si dovrebbe essere specificando come inferiore invece di limite superiore:

void foo(A<? super X> a, X x) 

In questo modo nessun errore del compilatore vengono prodotte, e tu hai la firma più generale applicabile.

+1

Questo non è esattamente quello che dice la domanda: l'OP chiede un modo per far sì che 'X' sia lo * stesso * tipo su 'A a' e' X x'. Forse vuole quello che hai risposto, ma la domanda dovrebbe essere riformulata allora. –

+0

OP chiede anche "Come si può ottenere una funzionalità simile senza errori?" - e la risposta di Marko fa proprio questo. – meriton

+0

@meriton, infatti. Ma sembra un po 'troppo vago, non specificato. –

Problemi correlati