Ya se señaló que no existe ni una solución ni una buena solución. Esto es lo que finalmente hice. Solo funciona para mi caso especial, pero puedes tomarlo como inspiración si te encuentras con problemas similares. (También explica por qué me encontré con este problema)
En primer lugar, existe esta clase (mostrando sólo la interfaz relevante):
class Pipe<Input, Output> {
boolean hasNext();
Input getNext();
void setNext(Output o);
}
La interfaz Foo
es en realidad
interface Processor<Input, Output> {
process(Pipe<Input, Output> p);
}
y la clase Bar
debería funcionar así
class JustCopyIt<Input, Output> implements Processor<Input, Output> {
process(Pipe<Input, Output> p) {
while (p.hasNext()) p.setNext(p.getNext());
}
}
La manera más fácil sería emitir los valores de esta manera: p.setNext((Output) p.getNext())
. Pero esto es malo, ya que permitiría crear una instancia de JustCopyIt<Integer, String>
. Llamar a este objeto fallaría misteriosamente en algún momento, pero no en el punto donde se comete el error real.
Hacer class JustCopyIt<Type> implements Processor<Type, Type>
tampoco funcionaría aquí, porque entonces no puedo procesar un Pipe<String, Object>
.
Así que lo que finalmente hice fue cambiar la interfaz a esto:
interface Processor<Input, Output> {
process(Pipe<? extends Input, ? super Output> p);
}
De esta manera, un JustCopyIt<List>
es capaz de procesar una Pipe<ArrayList, Collection>
.
Si bien esto técnicamente parece ser la única solución válida, sigue siendo malo porque 1) solo funciona para este caso especial, 2) me requirió cambiar la interfaz (lo que no siempre es posible) y 3) hizo el código de los otros procesadores feo.
Editar:
lectura Keiths respuesta del nuevo me inspiró otra solución:.
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
Yo no lo creo :( –
genéricos de Java son una implementación de plantillas de C++ recubiertos de caramelo, para un buen razón: compatibilidad con la base de códigos existente. No espere estar contento con ellos en todo momento. ¡No se burle de los genéricos divertidos! – DwB
@dwb: Huh: los genéricos de Java se comportan * completamente diferente * de las plantillas de C++. Por ejemplo, [plantilla la especialización falta por completo en Java] (http://stackoverflow.com/questions/3988180/java-generi cs-template-specialization-possible-overriding-template-types-with) (aunque parece que se han realizado algunas investigaciones teóricas en esa dirección. Y Java tampoco admite parámetros genéricos que no sean de tipo. –