Llamar explícitamente al método genérico

Si tengo una interfaz y una implementación:

interface BaseInterface{ }
class Base implements BaseInterface{ }

Y en otro lugar tengo un método:

<T extends Base> T foo() { /* return object of class T */ }

foo() debe ser llamado explícitamente con Base para asignar a la interfaz. Esto funciona bien:

BaseInterface a = <Base>foo();

Bien ... pero ¿cómo llamo explícitamente al siguiente método?

<V extends Base> Map<String, V> bar() { /* return object of Map<String, V> */ }

Ninguno de estos parece funcionar:

Map<String, ? extends BaseInterface> m = <Base>bar();
Map<String, ? extends BaseInterface> m = <String, Base>bar();
Map<String, ? extends BaseInterface> m = <Map<String, Base>>bar();

preguntado el 28 de agosto de 11 a las 00:08

Usa la sintaxis SomeClass<T> name = new SomeClass<T>(); -

Qué pasa: Map<String, Base> m = <Map<String, Base>>bar();? En su ejemplo, BaseInterface no es un subtipo de Base que causa el error. -

@Kru: exactamente, por lo tanto, debe llamar explícitamente al método diciéndole que desea un mapa aunque lo esté asignando a Map . Esa es la pregunta. -

@Mark: Tú no puedo asignar una variable de tipo Map<String, Base> a una variable de tipo Map<String, BaseInterface>. No puedes hacer Map<String, BaseInterface> m = new HashMap<String, Base>(), por ejemplo, por la misma razón que no puedes hacer List<Object> l = new ArrayList<String>(). Si pudiera hacer eso, sería posible insertar un BaseInterface objeto que no es un Base en el mapa, violando las restricciones genéricas. -

@CameronSkinner: No creo que eso sea cierto. En el ejemplo anterior BaseInterface a = <Base>foo(); es válida. Solo tiene que ser explícito sobre la implementación que desea del genérico. -

2 Respuestas

Creo que estás malinterpretando un poco cómo usar genéricos.

Lo que tienes ahora es esto:

<T extends Base> T foo() {...}

que, en inglés, significa que foo devolverá un objeto que es una subclase de Base (recuerde que subclase significa "esta clase o cualquier clase secundaria"). Pero foo no acepta argumentos, por lo que es imposible la implementación de foo saber qué T está obligado a, ni tampoco materia qué T está vinculado a: todo foo se preocupa, y lo único que les importa a las personas que llaman, es que devuelve algún tipo de Base.

Básicamente, lo que tienes es equivalente a esto:

Base foo() {...}

No es necesario utilizar genéricos en este caso porque foo no necesita preocuparse por la información de tipo: solo devuelve una subclase de Base. Sería diferente si estuviera haciendo esto:

<T extends Base> T doSomething(T input)

porque doSomething necesita la información de tipo genérico para decidir cuál será el tipo de retorno.

En lugar de declarar foo y bar como genéricos, ¿por qué no simplemente declararlos como

Base foo();
Map<String, Base> bar();

o mejor,

BaseInterface foo();
Map<String, BaseInterface> bar();

Ahora puede escribir su código de cliente:

BaseInterface a = foo();
Map<String, BaseInterface> m = bar();

Sé que esto no responde directamente a su pregunta, pero creo que en su caso (dado el código de ejemplo) realmente no necesita usar genéricos para resolver su problema. Es fácil confundir genéricos con polimorfismo. En este caso, el polimorfismo es tu amigo.

Respondido 28 ago 11, 04:08

He simplificado demasiado la pregunta para acortar su extensión. Me doy cuenta de que esto probablemente ha causado confusión. La verdadera pregunta es cómo llamo explícitamente al método <V extends Base> Map<String, V> bar(). - marca

@Mark: La respuesta sigue en pie: bar no acepta argumentos, por lo que no es necesario utilizar genéricos. Solo declara bar regresar Map<String, Base> y tendrás lo que quieras. - Cameron Skinner

@Cameron Skinner: No estoy de acuerdo con tu respuesta. Mire la declaración del Collections.emptyList(), .emptyMap(), etc. métodos. La declaracion es static <K,V> Map<K,V> emptyMap(). Por tu razonamiento, ¿por qué no lo hacen? static Map<Object,Object> emptyMap()? Porque entonces no podrás asignarlo a un Map<String,Integer> o algo así. Con la forma en que se declara, ahora puede hacer Map<String,Integer> foo = Collections.<String,Integer>emptyMap() - nuevoacct

Map<String, Base> m = bar<Base>();

De acuerdo con el código que publicó, el parámetro genérico V de la barra de método debe ser Base o una clase derivada de Base. BaseInterface no se deriva de Base. Es Base la que implementa BaseInterface.

Respondido 28 ago 11, 04:08

Mi mapa es Map<String, BaseInterface>... no puedo cambiarlo a Map<String, Base>. - marca

entonces eventualmente deberías cambiar la firma de la barra: Mapa bar() { } - Heisenbug

Creo que no comprende exactamente cómo funcionan los genéricos. De todos modos, V extiende Base y BaseInterface no extiende la base. Entonces, sea lo que sea lo que esté tratando de hacer, está bastante mal. - Heisenbug

Soy consciente de cómo funcionan los genéricos. Base implementa BaseInterface, por lo que es perfectamente aceptable asignar una Base a un objeto de BaseInterface. - marca

sí, pero para hacer esto no necesita genéricos. por favor lea atentamente la respuesta de @Cameron Skinner. Lo ha explicado bien. - Heisenbug

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.