È stato già rilevato che non esiste né una soluzione né una soluzione piacevole. Ecco cosa ho finalmente fatto. Funziona solo per il mio caso speciale, ma puoi prenderlo come ispirazione se incontri problemi simili. (Ciò spiega anche il motivo per cui mi sono imbattuto in questo problema)
Prima di tutto, v'è questa classe (che mostra solo l'interfaccia in questione):
class Pipe<Input, Output> {
boolean hasNext();
Input getNext();
void setNext(Output o);
}
L'interfaccia Foo
è in realtà
interface Processor<Input, Output> {
process(Pipe<Input, Output> p);
}
e la classe Bar
dovrebbe funzionare in questo modo
class JustCopyIt<Input, Output> implements Processor<Input, Output> {
process(Pipe<Input, Output> p) {
while (p.hasNext()) p.setNext(p.getNext());
}
}
Il modo più semplice sarebbe quello di trasmettere valori come questo: p.setNext((Output) p.getNext())
. Ma questo è male in quanto consentirebbe di creare un'istanza di JustCopyIt<Integer, String>
. Chiamare questo oggetto fallirebbe misteriosamente ad un certo punto, ma non nel punto in cui viene fatto l'errore effettivo.
Fare anche class JustCopyIt<Type> implements Processor<Type, Type>
non funzionerebbe anche qui, perché quindi non sono in grado di elaborare un Pipe<String, Object>
.
Così che alla fine ho fatto è stato per modificare l'interfaccia a questo:
interface Processor<Input, Output> {
process(Pipe<? extends Input, ? super Output> p);
}
In questo modo, un JustCopyIt<List>
è in grado di elaborare un Pipe<ArrayList, Collection>
.
Mentre questa tecnica sembra essere l'unica soluzione valida, è ancora male perché 1) funziona solo per questo caso speciale, 2) mi ha richiesto di cambiare l'interfaccia (che non è sempre possibile) e 3) ha fatto il codice degli altri processori brutto.
Edit:
lettura Keiths risposta ancora una volta mi ha ispirato per un'altra soluzione:.
public abstract class Bar<A, B> implements Foo<A, B> {
public static <B, A extends B> Bar<A, B> newInstance() {
return new BarImpl<B, A>();
}
private static class BarImpl<B, A extends B> extends Bar<A, B> {
// code goes here
}
}
// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error
Io non la penso così :( –
generici Java sono un'implementazione di modelli C++ caramelle rivestite, per una buona ragione - retrocompatibilità con la base di codice esistente.Non ti aspetti di essere felice di loro in ogni momento.Non prendi in giro generici divertenti e allegri! – DwB
@dwb: Huh? I generici Java si comportano * in modo completamente diverso * dai modelli C++. la specializzazione è completamente mancante da Java] (http://stackoverflow.com/questions/3988180/java-generi cs-template-specialization-possible-overriding-template-types-with) (anche se sembra che alcune ricerche teoriche siano state fatte in quella direzione. E Java non supporta neanche i parametri generici non di tipo. –