Java comodines genéricos con múltiples clases

385

Quiero tener un objeto Class, pero quiero forzar cualquier clase que represente para extender la clase A e implementar la interfaz B.

Puedo hacer:

Class<? extends ClassA>

O:

Class<? extends InterfaceB>

Pero no puedo hacer las dos cosas. ¿Hay alguna forma de hacer esto?

Alex Beardsley
fuente

Respuestas:

614

En realidad, usted puede hacer lo que quiera. Si desea proporcionar múltiples interfaces o una clase más interfaces, debe hacer que su comodín se vea así:

<T extends ClassA & InterfaceB>

Consulte el Tutorial de genéricos en sun.com, específicamente la sección Parámetros de tipo acotado , en la parte inferior de la página. En realidad, puede enumerar más de una interfaz si lo desea, utilizando & InterfaceNamepara cada una que necesita.

Esto puede ser arbitrariamente complicado. Para demostrarlo, vea la declaración JavaDoc de Collections#max, que (envuelto en dos líneas) es:

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

¿Por qué tan complicado? Como se dijo en las preguntas frecuentes de Java Generics: para preservar la compatibilidad binaria .

Parece que esto no funciona para la declaración de variables, pero funciona cuando se pone un límite genérico en una clase. Por lo tanto, para hacer lo que quiera, puede que tenga que saltar algunos aros. Pero puedes hacerlo. Puede hacer algo como esto, poniendo un límite genérico en su clase y luego:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

conseguir variableeso tiene la restricción que quieres. Para obtener más información y ejemplos, consulte la página 3 de Genéricos en Java 5.0 . Tenga en cuenta que, en <T extends B & C>, el nombre de la clase debe venir primero, y las interfaces siguen. Y, por supuesto, solo puede enumerar una sola clase.

Eddie
fuente
95
Esto es útil. Vale la pena mencionar que la clase debe ser lo primero, no puede decir '<T extiende InterfaceB y ClassA>'.
EricS
55
¿Cómo haces lo mismo para que T amplíe una clase O implemente una interfaz?
Ragunath Jawahar
1
@RagunathJawahar: No puedes ser demasiado abierto sobre esto. Debe tener algunos límites, y uno de ellos es saber de antemano dónde tendrá interfaces y dónde tendrá clases, y cómo usará la herencia. Debe saber para un parámetro de tipo si será una clase o una interfaz.
Eddie
44
La primera línea de su respuesta dice "haga que su comodín se vea más o menos así". No hay ?en esa expresión, entonces, ¿es realmente un "comodín"? Pregunto porque no puedo hacer que todo el concepto de "extender dos cosas a la vez" funcione cuando el parámetro de tipo realmente es un comodín.
The111
1
Sí, en realidad no es un comodín y no puede hacer lo que el OP le pidió con un comodín. Usted puede hacer lo que el PO quería, efectivamente, no sólo con un comodín.
Eddie
17

No puede hacerlo con parámetros de tipo "anónimos" (es decir, comodines que usan ?), pero puede hacerlo con parámetros de tipo "con nombre". Simplemente declare el parámetro de tipo a nivel de método o clase.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}
usuario2861738
fuente
2
¿Por qué no se permiten comodines? es decir, no veo por qué esto no debería ser válido: ArrayList <? extiende la clase A y la interfaz B> lista; Y luego, si sacó un elemento de la lista, podría asignarlo a una variable de tipo Clase A o de tipo Interfaz B
Marque el
¡Reemplazar el comodín con una variable es una gran técnica! Pero no siempre funciona. Por ejemplo, Java no permite variables de tipo de nombre en tipos de valor de anotación, mientras que permite comodines.
firmado