thomas programación programacion orientada objetos libro introducción fundamentos ejemplo desde con codigos cero java generics subclass superclass

programación - ¿Entender los límites superior e inferior en? en Java Genéricos



libro de programacion en java netbeans pdf (6)

? como parámetro de tipo solo puede ser usado en métodos. por ejemplo: 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 das ? extends Serializable ? extends Serializable se proporciona 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 que una llamada a printAll se compilará solo si se pasa una MyList con algún tipo genérico que es o implementa 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 acotado con super es un límite inferior . Así que podríamos decir que una llamada a printAll se compilará solo si se pasa una MyList con algún tipo genérico que es MyClass o algún MyClass de MyClass . Entonces, en este caso, aceptaría MyList<MyClass> , por ejemplo, MyList<MyParentClass> , o MyList<Object> .

Entonces, di si MyClass se parece a:

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

luego, 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 parezca que está definiendo el comportamiento en tiempo de ejecución: los genéricos se basan en la verificación del tiempo de compilación. Por ejemplo, ¿no podría pasar un MyList<MySubclass> como un argumento para MyList<? super MyClass> MyList<? super MyClass> , aunque podría contener instancias de MyClass , por herencia. Lo reescribiría a:

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

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

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

? extends T ? extends T significa cualquier clase que extienda T Por lo tanto, nos referimos 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.

? super T ? super T significa cualquier clase / interfaz que sea super de T. Por lo tanto, nos referimos a todos los padres de T. T es el límite inferior. La clase más baja en la jerarquía de herencia.

Cerca, pero no diría "hijos de T " o "padres de T ", ya que estos límites son inclusivos , sería más exacto decir " T o sus subtipos" y " T o sus supertipos".

Realmente me está costando entender el parámetro de comodín. Tengo algunas preguntas al respecto.

  1. ? como parámetro de tipo solo puede ser usado en métodos. por ejemplo: 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 imprimirá MyList si tiene objetos que implementan la interfaz Serialzable " .
    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 ) " .

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 utilizar 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> MyList<? super MyClass> significa que puedo imprimir MyList si tiene objetos de MyClass o cualquiera de las interfaces o clases que implementa. Es decir, MyClass es un límite inferior . Es la última clase en la jerarquía de herencia. Esto significa que mi suposición inicial era errónea.

Entonces, di si MyClass parece a:

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

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

Actualización 2:

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

  1. ? extends T ? extends T significa cualquier clase que extienda T Así, nos referimos 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 ? super T significa cualquier clase / interfaz que sea super de T Así nos referimos a todos los padres de T T es, pues, el límite inferior. La clase más baja en la jerarquía de herencia.


En primer lugar, T o E o K o lo que no sean nombres fijos. Son solo variables de tipo, y tú decides el nombre para ellas. T , E , K son solo ejemplos, pero podrías llamarlo Foo o lo que sea.

Ahora vamos a tu primera pregunta: ¿desde 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 comodines en los parámetros de los métodos o en las variables cuando no te importa el tipo.

Ahora, con respecto a su segunda pregunta: el límite inferior brinda aún más flexibilidad a sus métodos genéricos. Ambos se extends y super son lo opuesto:

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

Lo último puede ser útil cuando desea aceptar un tipo que sea compatible con T (de modo que T es un tipo de ese tipo). Un ejemplo práctico se puede encontrar here .


Hmmmm su declaración sobre super ( printAll(MyList<? super MyClass>) ) no está clara. Lo que significa, suponiendo que Myclass extienda el Object es que puede printAll(MyList<MyClass>) y puede printAll(MyList<Object>) pero nada más ... 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 de mayúsculas de una sola letra, y T se usa a menudo para métodos genéricos y E para clases ...


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

void <?> foo() {}

? se utiliza para enlazar a otros genéricos sin proporcionar el parámetro de tipo. Puedes escribir por 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, como usted dice, no imprimirá la lista "si tiene objetos de MyClass". Puede tener objetos de cualquier clase que sea una subclase de una clase que sea un padre de MyClass. El compilador no sabe en el momento de la compilación cuáles son los objetos que estarán en la lista de todos modos.

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

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

Luego puedes llamar a ese método con una 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, por 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); }


Vamos a empezar desde el principio.

Hablando estrictamente, cualquier identificador de 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 perfectamente válido Java.

A continuación, puede utilizar ? En cualquier lugar donde pueda realizar una declaración. Puede usar el comodín cuando declara variables, pero no cuando las crea :

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) { } }

De nuevo todo es válido, no puedes hacer esto:

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

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

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 de demostración se extends : el límite más estrecho que puede asumir de la Collection es Runnable ya que el usuario puede pasar una Collection de cualquier cosa que tenga Runnable en su jerarquía de herencia.

El segundo ejemplo de ejemplo muestra super : el límite más estrecho que puede asumir de la Collection es el Object ya que permitimos cualquier cosa que esté en la jerarquía de herencia de Runnable .


? También se puede utilizar como método de retorno.

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

o como un campo o tipo de variable

List<? extends Number> xxx = ... List<? super Nember> xxx = ...