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
- Hay objetos de MyClass en la lista.
- 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:
-
MyList<MyClass>
-
MyList<Thread>
-
MyList<Runnable>
-
MyList<ActionListener>
-
MyList<EventListener>
-
MyList<Object>
-
MyList<? super X>
MyList<? super X>
dondeX
esMyClass
,Thread
,Runnable
,ActionListener
,EventListener
uObject
.
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 seasuper
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.
?
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 tipoEntiendo el límite superior en
?
.printAll(MyList<? extends Serializable>)
significa: "printAll
imprimiráMyList
si tiene objetos que implementan la interfazSerialzable
" .
Tengo un pequeño problema con elsuper
.printAll(MyList<? super MyClass>)
significa: "printAll
imprimiráMyList
si tiene objetos deMyClass
o cualquier clase que extiendaMyClass
(los descendientes deMyClass
) " .
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.
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
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 extiendaT
Así, nos referimos a los hijos deT
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 seasuper
deT
Así nos referimos a todos los padres deT
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 deT
-
? super T
? super T
: un tipo desconocido que es un super tipo deT
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 = ...