Súper palabra clave genérica de Java
Repasé estos temas
- ¿Genéricos...? Súper T
- Delimitar genéricos con la palabra clave 'super'
Sin embargo, todavía parece que estoy un poco perdido con super
la palabra clave:
Cuando declaramos una colección como esa:
List<? super Number> list = null; list.add(new Integer(0)); // this compiles list.add(new Object()); // this doesn't compile
¿No debería ser lo contrario? Tenemos una lista que contiene algunos objetos (de tipo desconocido) que son padres de Number
. Entonces Object
debería encajar (ya que es el padre de Number
) y Integer
no debería. Por alguna razón ocurre lo contrario.
Siempre que tengamos el siguiente código
static void test(List<? super Number> param) { param.add(new Integer(2)); } public static void main(String[] args) { List<String> sList = new ArrayList<String>(); test(sList); // will never compile, however... }
Es imposible compilar el código anterior (y mi cordura sugiere que este es el comportamiento correcto), pero la lógica básica podría demostrar lo contrario:
String is Object, Object is superclass of Number. So String should work.
Sé que esto es una locura, pero ¿no es esta la razón por la que no permitieron <S super T>
construcciones? En caso afirmativo, ¿por qué <? super T>
está permitido?
¿Alguien podría ayudarme a restaurar la parte que falta de esta cadena lógica?
El comodín acotado en List<? super Number>
puede capturar Number
y cualquiera de sus supertipos. Desde Number extends Object implements Serializable
, esto significa que los únicos tipos que actualmente son convertibles en captura List<? super Number>
son:
List<Number>
List<Object>
List<Serializable>
Tenga en cuenta que puede hacerlo add(Integer.valueOf(0))
con cualquiera de los tipos anteriores. sin embargo, NO PUEDES add(new Object())
usar a List<Number>
o a List<Serializable>
, ya que eso viola la regla de seguridad de tipo genérico.
Por lo tanto, NO es cierto que se pueda utilizar add
cualquier supertipo de Number
a List<? super Number>
; Simplemente no es así como funcionan la conversión de captura y comodín limitado. No declaras a List<? super Number>
porque quizás quieras agregarle un Object
(¡no puedes!); lo hace porque quiere agregarle Number
objetos (es decir, es un "consumidor" de Number
), y simplemente a List<Number>
es demasiado restrictivo.
Referencias
- Preguntas frecuentes sobre genéricos de Angelika Langer
- ¿Qué es un comodín acotado?
- ¿Cuándo debería utilizar un tipo parametrizado comodín con un límite inferior? ("Cuando un tipo parametrizado concreto sería demasiado restrictivo").
- ¿Por qué no existe un límite inferior para los parámetros de tipo? ("Porque no tiene sentido").
- Conversión de captura JLS 5.1.10
Ver también
- Efectivo Java 2.ª edición , elemento 28: utilice comodines acotados para aumentar la flexibilidad de la API
- "PECS significa productor-
extends
, consumidor-super
- "PECS significa productor-
Preguntas relacionadas
- Demasiados para enumerarlos, PECS,
new Integer(0)
vsvalueOf
, etc.
La primera parte List<Number>
encaja, List<? super Number>
pero no puedes agregar un Object
archivo List<Number>
. Es por eso que no puedes agregar un Object
a List<? super Number>
.
Por otro lado, puedes agregar todas las subclases de Number
( Number
incluido) a tu lista.
Para la segunda parte, String
es una Object
, pero String
no es una superclase de Number
.
Si funcionara así, como cada clase es una subclase de Object
, super
no tendría significado.
Veamos todos los casos posibles con List<? super Number>
:
- La lista aprobada es una
List<Object>
List<Object>
trabajaráObject
encaja<? super Number>
- Puede agregar cualquier subtipo de
Number
a unList<Object>
- Incluso si también pudieras agregarlo,
String
lo único de lo que estás seguro es que puedes agregar cualquier subclase deNumber
.
- La lista aprobada es una
List<Number>
:List<Number>
trabajaráNumber
encaja<? super Number>
- Puede agregar cualquier subtipo de
Number
a unList<Number>
- La lista pasada es una
List<Integer>
(o cualquier subclase deNumber
):List<Integer>
no funcionará- Integer es una subclase de
Number
, por lo que es exactamente lo que queremos evitar. - Incluso si un
Integer
encaja en a,Number
no podrá agregar ninguna subclase deNumber
en aList<Integer>
(por ejemplo, aFloat
) super
no significa una subclase.
- La lista pasada es una
List<String>
(o cualquier clase que no se extiendaNumber
ni esté en la "súper jerarquía" deNumber
(es decir,Number
yObject
):List<String>
no funcionaráString
no encaja en laNumber
"superjerarquía"- Incluso si
String
encajaObject
(que es una superclase deNumber
), no estará seguro de poder agregar aNumber
aList
que contenga cualquier subclase de una de las superclases deNumber
). super
no significa ninguna subclase de una de las superclases, solo significa una de las superclases.
Como funciona ?
Se podría decir que siempre que pueda agregar cualquier subclase Number
con lo escrito List
, respeta la super
palabra clave.
No lo entendí por un tiempo. Muchas de las respuestas aquí y las otras preguntas muestran específicamente cuándo y dónde ciertos usos son errores, pero no tanto por qué.
Así fue como finalmente lo conseguí. Si tengo una función que agrega Number
s a a List
, es posible que desee agregarlas del tipo MySuperEfficientNumber
que es mi propia clase personalizada que implementa Number
(pero no es una subclase de Integer
). Ahora bien, es posible que la persona que llama no sepa nada sobre MySuperEfficientNumber
, pero siempre que sepa tratar los elementos agregados a la lista como nada más específico que Number
, estará bien.
Si declaré mi método como:
public static void addNumbersToList(List<? extends Number> numbers)
Entonces la persona que llama podría pasar un archivo List<Integer>
. Si mi método agregara a MySuperEfficientNumber
al final de numbers
, entonces la persona que llama ya no tendría a List
of Integer
s y el siguiente código no funcionaría:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
Obviamente esto no puede funcionar. Y el error estaría dentro del addNumbersToList
método. Obtendrías algo como:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Porque numbers
podría ser cualquier tipo específico Number
, no necesariamente algo MySuperEfficientNumber
con lo que sea compatible. Si invirtiera la declaración para usar super
, el método se compilaría sin errores, pero el código de la persona que llama fallaría con:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
Porque mi método dice: "No creas que puedes List
ser de algo más específico que Number
. Podría agregar todo tipo de Number
mensajes raros a la lista, pero tendrás que lidiar con ello. Si quieres pensar en ellos como algo incluso más general que Number
... Object
está bien, te garantizo que serán al menos Number
s, pero puedes tratarlos de manera más general si quieres".
Mientras que extends
dice: "Realmente no me importa qué tipo de List
s me das, siempre y cuando cada elemento sea al menos un Number
. Puede ser cualquier tipo de Number
, incluso tus propios s extraños, personalizados e inventados Number
. Siempre que "Si implementan esa interfaz, estamos bien. No voy a agregar nada a tu lista ya que no sé qué tipo concreto estás usando allí".