Genéricos de Java: Make Generic to extends 2 interfaces

¿Cómo haces que esto funcione?

public class Frankenstein{ } 

Sin hacer

 public interface Weirdo extends Ihuman, IMonster{ } 

Editar

¿Por qué esto no funciona?

 public  void mapThis( Class<? extends MyClass> key, Class value) { } 

Estoy obteniendo un mensaje de comstackción marcando Class Class como un Error.

Reimeus ya señaló que lo que estás pidiendo en tu edición no es posible. Me gustaría expandirme un poco sobre por qué.

Uno pensaría que podrías usar lo siguiente:

 public  void mapThis( Class> key, Class value ) { ... } 

De hecho, eso es lo que me vino a la mente cuando vi esta publicación por primera vez. Pero esto realmente da un error de comstackción:

una variable de tipo no puede ser seguida por otros límites

Para ayudarme a explicar por qué, me gustaría citar una publicación de Oracle Blogs de Victor Rudometov sobre este error:

Este hecho no siempre es claro, pero es cierto. El siguiente código no debe comstackrse:

interface I {}

class TestBounds {

}

Debido a que JLS Capítulo 4 Tipos, valores y variables sección 4.4 Tipo de variables establece: ” El límite consiste en una variable de tipo, o una clase o tipo de interfaz T posiblemente seguida por otros tipos de interfaz I 1 , …, I n. . Entonces uno puede usar T extiende U, T extiende SomeClass & I , pero no T extiende U & I. Esta regla se aplica a todos los casos, incluidas las variables de tipo y los límites en los métodos y constructores.

Las razones para esta restricción se exploran en una publicación estrechamente relacionada: ¿Por qué no puedo usar un argumento de tipo en un parámetro de tipo con múltiples límites?

En resumen, la restricción se impuso con el fin de “evitar ciertas situaciones incómodas que comienzan a existir” ( JLS §4.9 ).

¿Qué tipo de situaciones incómodas? Una respuesta de Chris Povirk describe una:

[Una razón para la restricción es] la posibilidad de especificar tipos ilegales. Específicamente, extender una interfaz genérica dos veces con diferentes parámetros. No puedo proponer un ejemplo no artificial, pero:

 /** Contains a Comparator that also implements the given type T. */ class StringComparatorHolder> { private final C comparator; // ... } void foo(StringComparatorHolder, ?> holder) { ... } 

Ahora holder.comparator es un Comparator y un Comparator .

Chris también apunta a Sun bug 4899305 , que era un error que impugnaba esta restricción de idioma. Se cerró como No se Corrige con el siguiente comentario:

Si una variable de tipo pudiera ser seguida por variables de tipo o por interfaces (posiblemente parametrizadas), probablemente habría más variables de tipo mutuamente recursivas, que son muy difíciles de manejar. Las cosas ya son complicadas cuando un límite es simplemente un tipo parametrizado, por ejemplo, > . En consecuencia, los límites no van a cambiar ahora. Tanto javac como Eclipse aceptan que S&T y S&Comparable son ilegales.

Entonces esas son las razones detrás de la restricción. Dirigiéndose específicamente a los métodos generics (a lo que se refiere su pregunta), me gustaría señalar que la inferencia de tipo teóricamente haría que tales límites fueran inútiles de todos modos.

Si reexaminamos los parámetros de tipo declarados en la firma hipotética anterior:

  

Suponiendo que la persona que llama no especifica explícitamente T y U , esto se puede reducir a lo siguiente:

  

O solo esto (sutil diferencia, pero ese es otro tema ):

  

Esto se debe a que T no tiene ningún límite, por lo que no importa qué tipo de argumentos se pasen, T siempre puede resolver Object como mínimo, y entonces U puede hacerlo.

Volvamos y digamos que T está limitado:

  

Esto se puede reducir de la misma manera ( Foo podría ser una clase o interfaz):

  

En función de ese razonamiento, la syntax que está tratando de lograr no tiene sentido en cuanto a restringir a la persona que llama a argumentos más específicos.

Adenda previa a Java 8:

Antes de Java 8, hay un caso de uso para lo que estás tratando de hacer. Debido a una limitación de cómo el comstackdor infiere los parámetros de tipo de método genérico, mi razonamiento anterior para salir por la ventana. Tome el siguiente método genérico:

 class MyClass { static  void foo(T t1, T t2) { } } 

Este es el error común de un principiante de tratar de hacer un método que toma dos parámetros del “mismo tipo”. Por supuesto, no tiene sentido por la forma en que funciona la herencia:

 MyClass.foo("asdf", 42); // legal 

Aquí, se infiere que T es un Object ; esto coincide con el razonamiento anterior sobre la simplificación del mapThis . mapThis parámetros de tipo. Debe especificar manualmente los parámetros de tipo para lograr la verificación de tipo prevista:

 MyClass.foo("asdf", 42); // compiler error 

Sin embargo, y aquí es donde su caso de uso comienza a aparecer, es una cuestión diferente con múltiples parámetros de tipo con límites escalonados:

 class MyClass { static  void foo(T t, U u) { } } 

Ahora esta llamada errores:

 MyClass.foo("asdf", 42); // compiler error 

Las tablas han cambiado, tenemos que relajar manualmente los parámetros de tipo para que compile:

 MyClass.foo("asdf", 42); // legal 

Esto sucede debido a la forma limitada en que el comstackdor infiere los parámetros de tipo de método. Por esta razón, lo que quería lograr tendría una aplicación para restringir los argumentos de la persona que llama.

Sin embargo, este problema parece haberse resuelto en Java 8, y MyClass.foo("asdf", 42) ahora se comstack sin ningún error (gracias a Regent por señalar esto).

Solo pensé en compartir mi hack que uso en estas situaciones (bastante raras):

  /** * This is a type-checking method, which gets around Java's inability * to handle multiple bounds such as "V extends T & ContextAware". * * @param value initial value, which should extends T * @param contextAware initial value, which should extend ContextAware * @return a new proxy object */ public T blah(T value, ContextAware contextAware) { if (value != contextAware) { throw new IllegalArgumentException("This method expects the same object for both parameters."); } return blah(value); } 

Entonces, al requerir el mismo objeto para cada uno de los límites que intentas satisfacer, obtienes la verificación de tipo de tiempo de comstackción y el único objeto que hace todo. Es cierto que es un poco tonto pasar el mismo objeto para cada parámetro, pero lo hago en mi código “interno” con bastante seguridad y comodidad.