¿Entendiendo los límites superior e inferior? en Java Generics


Realmente estoy teniendo dificultades para entender el parámetro comodín. Tengo algunas preguntas con respecto a eso.

  1. ? como parámetro de tipo solo se puede usar en métodos. eg: printAll(MyList<? extends Serializable>) No puedo definir clases con ? como parámetro de tipo.

  2. Entiendo el límite superior en ?. printAll(MyList<? extends Serializable>) significa: "printAll se imprimirá MyList si tiene objetos que implementan la interfaz Serialzable."
    Tengo un pequeño problema con el super. printAll(MyList<? super MyClass>) significa: "printAll se imprimirá MyList si tiene objetos de MyClass o cualquier clase que se extienda MyClass (los descendientes de MyClass)."

Corrígeme donde me equivoqué.

En resumen, solo T o E o K o V o N pueden usarse como parámetros de tipo para definir clases genéricas. ? solo se puede usar en métodos


Actualización 1:
public void printAll(MyList<? super MyClass>){
    // code code code
}

De acuerdo con el libro de Ivor Horton, MyList<? super MyClass> significa que puedo imprimir MyList si tiene objetos de MyClass o cualquiera de las interfaces o clases que implementa. Es decir, MyClasses un límite inferior. Es la última clase en la jerarquía de herencia. Esto significa que mi suposición inicial estaba equivocada.

Entonces, digamos que si MyClass parece:

public class MyClass extends Thread implements ActionListener{
    // whatever
}

Entonces, printAll() se imprimirá si
1. Hay objetos de MyClass en la lista
2. Hay objetos de Thread o ActionListener en el List


Actualización 2:

Así, después de haber leído el muchas respuestas a la pregunta, aquí está mi entendimiento:

  1. ? extends T significa cualquier clase que se extiende T. Por lo tanto, nos estamos refiriendo a los hijos de T. Por lo tanto, T es el límite superior. La clase más alta en la jerarquía de herencia

  2. ? super T significa cualquier clase / interfaz que sea super de T. Así nos estamos refiriendo a todos los padres de T. T es así el menor obligado. La clase más baja en la jerarquía de herencia

Author: Lonely Neuron, 2013-11-05

6 answers

? como parámetro de tipo solo se puede usar en métodos. eg: printAll(MyList<? extends Serializable>) No puedo definir clases con ? como parámetro de tipo.

Un comodín (?) no es un parámetro de tipo formal, sino que se puede usar como un argumento de tipo . En el ejemplo que da, ? extends Serializable se da como un argumento de tipo al tipo genérico MyList, del parámetro del método printAll.

Los métodos también pueden declarar parámetros de tipo como clases, por ejemplo:

static <T extends Serializable> void printAll(MyList<T> myList)

Entiendo el límite superior en ?. printAll(MyList<? extends Serializable>) significa printAll imprimirá myList si tiene objetos que implementan la interfaz Serialzable

Más exactamente, significa una llamada a printAll se compilará solo si se le pasa un MyList con algún tipo genérico que sea o implemente Serializable. En este caso aceptaría un MyList<Serializable>, MyList<Integer>, etc.

Tengo un pequeño problema con el super. printAll(MyList<? super MyClass>) significa printAll imprimirá myList si tiene objetos de MyClass o cualquier clase que extienda MyClass (los descendientes de MyClass)

Un comodín limitado con super es un inferior limitado. Así que podríamos decir que una llamada a printAll se compilará solo si se le pasa un MyList con algún tipo genérico que sea MyClass o algún supertipo de MyClass. Así que en este caso aceptaría MyList<MyClass>, por ejemplo MyList<MyParentClass>, o MyList<Object>.

Entonces, digamos que si MyClass se parece a: {[54]]}

public class MyClass extends Thread implements ActionListener{
    // whatever
}

Entonces, printAll () imprimirá si

  1. Hay objetos de MyClass en la lista
  2. Hay objetos de Thread o ActionListener en la lista

Estás en el camino correcto. Pero creo que decir, por ejemplo, "se imprimirá si hay objetos de MyClass en la lista" es problemático. Eso hace que suene como si estuviera definiendo el comportamiento de tiempo de ejecución: los genéricos se basan en comprobaciones de tiempo de compilación. Por ejemplo, no sería capaz de pasar un MyList<MySubclass> como argumento para MyList<? super MyClass>, aunque podría contener instancias de MyClass, por herencia. Lo reformularía de la siguiente manera:

Una llamada a printAll(MyList<? super MyClass>) se compilará solo si se le pasa a:

  1. MyList<MyClass>
  2. MyList<Thread>
  3. MyList<Runnable>
  4. MyList<ActionListener>
  5. MyList<EventListener>
  6. MyList<Object>
  7. MyList<? super X> donde X es MyClass, Thread, Runnable, ActionListener, EventListener, o Object.

Así que, después de haber leído las muchas respuestas a la pregunta, aquí está mi comprensión:

? extends T significa cualquiera clase que extiende T . Por lo tanto, nos estamos refiriendo a los hijos de T. Por lo tanto, T es el límite superior. La clase más alta en la jerarquía de la herencia

? super T significa cualquier clase / interfaz que es super de T. Así somos refiriéndose a todos los padres de T. T es así el límite inferior. El la clase más baja en la jerarquía de herencia

Cerrar, pero yo no diría "hijos de T "o" padres de T", ya que estos límites son incluido - sería más exacto decir "T o sus subtipos", y "T o su tierra".

 14
Author: Paul Bellora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 19:28:07

En primer lugar T o E o K o lo que sea no son nombres fijos. Son solo variables de tipo, y usted decide el nombre para ellos. T, E, K son solo ejemplos, pero se podría llamar Foo o lo que sea.

Ahora vamos a tu primera pregunta: dado que el comodín ? representa el tipo "cualquiera y desconocido", el no especificado, no tiene ningún sentido declarar una clase genérica sobre un tipo no especificado. Es útil tener comodín en parámetros de métodos o en variables cuando no te importa el tipo.

Ahora con respecto a su segunda pregunta: el límite inferior da aún más flexibilidad a sus métodos genéricos. tanto extends como super son lo contrario:

  • ? extends T: un tipo desconocido que es un subtipo de T
  • ? super T: un tipo desconocido que es un supertipo de T

Este último puede ser útil cuando se quiere aceptar un tipo que es compatible con T (de modo que T es-a ese tipo). Un ejemplo práctico puede se encuentra aquí.

 11
Author: Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 18:17:46

Comencemos desde el principio.

Estrictamente hablando cualquier identificador java válido se puede usar como un parámetro de tipo genérico , es solo un tipo especial de variable:

public static final class MyGenericClass<MyGenericType> {

}

Es Java perfectamente válido.

A continuación, puede usar ? en cualquier lugar donde pueda hacer una declaración. Puedes usar el comodín cuando declaras variables pero no cuando instancias:

public static final class MyGenericClass {
    private final Collection<? extends String> myThings;

    public MyGenericClass(Collection<? extends String> myThings) {
        this.myThings = myThings;
    }  

    public void doStuff(final Collection<? extends String> myThings) {

    }
}

Es de nuevo todo válido, usted no puede hacer esto:

final Collection<? extends String> myThings = new ArrayList<? extends String>();

Cuando se trata de extends vs super esto se llama covarianza vs contra-varianza. Determina qué dirección a lo largo de la jerarquía de clases a los tipos suministrados se les permite viajar:

final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();

Los dos primeros ejemplos demuestran extends - el límite más ajustado que se puede asumir de Collection es Runnable ya que el usuario puede pasar un Collection de cualquier cosa que tenga Runnable en su jerarquía de herencia.

Los dos segundos ejemplos demuestran super - el límite más apretado que puede asumir desde el Collection es Object como permitimos cualquier cosa que es en la jerarquía de herencia de Runnable.

 1
Author: Boris the Spider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 18:32:40

Hmmmm su declaración en super ( printAll(MyList<? super MyClass>) ) no está claro. Lo que significa, suponiendo que Myclass se extiende Object es que puedes printAll(MyList<MyClass>) y puedes printAll(MyList<Object>) pero nada más.... esto significa que el tipo genérico de myList tiene que ser una superclase (no una subclase) de MyClass. Esto es diferente a lo que dijiste.

En cuanto a la T, E, K, V, o N, bueno, estos son nombres sin sentido en sí mismos. Puedes usar lo que quieras. Sin embargo, la convención sugiere valores en mayúsculas de una sola letra, y T es a menudo usado para métodos genéricos, y E para clases....

 0
Author: rolfl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 18:19:06

Para la primera pregunta: tampoco se puede definir un método con ? como parámetro de tipo. Lo siguiente no compilará:

void <?> foo() {}

? se utiliza para enlazar a otros genéricos sin proporcionar el parámetro type. Puedes escribir para métodos:

void foo(List<?> e) {}

Y también puedes escribir para las clases:

public class Bar<E extends List<?>> { }

Para el uso de super:

public void printAll(MyList<? super MyClass>){
    // code code code
}

Esto no imprimirá como usted dice la lista "si tiene objetos de MyClass". Puede tener objetos de cualquier clase que sea un subclase de una clase que es un padre de MyClass. El compilador no sabe en tiempo de compilación cuáles son los objetos que estarán en la lista de todos modos.

Para entenderlo, considere un ejemplo simple con la jerarquía de clases Number. Float y Integer son hijos de Number. Puedes escribir tu método así:

public void printAll(List<? super Float>){
    // code code code
}

Entonces puedes llamar a ese método con un List<Number>:

List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.

Esto es posible no sería super útil en ese caso. Donde sería útil para un ejemplo es cuando desea agregar Float a una colección sin querer que sea solo una Lista, como:

public void addFloat(List<? super Float> list){
    list.add(2.5);
}
 0
Author: Cyrille Ka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 18:31:39

? también se puede utilizar como método return type

List<? extends Number> xxx() {
    ...
}

O como campo o tipo variable

List<? extends Number> xxx = ...
List<? super Nember> xxx = ...
 0
Author: Evgeniy Dorofeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-14 10:13:16