superclase - super() java
java genéricos super palabra clave (6)
Revisé estos temas
Sin embargo, todavía parezco perdido con la palabra clave super
:
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 los padres de
Number
. Así queObject
debería caber (ya que es el padre delNumber
), yInteger
no debería. Lo contrario es el caso por alguna razón.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) { ArrayList<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 construcciones
<S super T>
? Si es así, ¿por qué<? super T>
<? super T>
está permitido?
¿Alguien podría ayudarme a restaurar la parte que falta de esta cadena lógica?
El comodín delimitado en List<? super Number>
List<? super Number>
puede capturar Number
y cualquiera de sus supertipos. Dado que Number extends Object implements Serializable
, esto significa que los únicos tipos que actualmente se pueden capturar son convertibles mediante List<? super Number>
List<? super Number>
son:
-
List<Number>
-
List<Object>
-
List<Serializable>
Tenga en cuenta que puede add(Integer.valueOf(0))
a cualquiera de los tipos anteriores. sin embargo, NO PUEDE add(new Object())
a una List<Number>
o una List<Serializable>
, ya que eso infringe la regla de seguridad de tipo genérico.
Por lo tanto, NO es cierto que pueda add
un supertipo de Number
a una List<? super Number>
List<? super Number>
; simplemente no es así cómo funciona la conversión de comodines y captura delimitada. Usted no declara una List<? super Number>
List<? super Number>
porque es posible que desee agregarle un Object
(¡no puede!); lo haces porque deseas agregarle objetos Number
(es decir, es un "consumidor" de Number
), y simplemente una List<Number>
es demasiado restrictiva.
Referencias
- Preguntas frecuentes sobre genéricos de Angelika Langer
- ¿Qué es un comodín delimitado?
- ¿Cuándo usaría un tipo de comodín parametrizado con un límite inferior? ("Cuando un tipo parametrizado concreto sería demasiado restrictivo").
- ¿Por qué no hay un límite inferior para los parámetros de tipo? ("Porque no tiene sentido").
- JLS 5.1.10 Conversión de captura
Ver también
- Effective Java 2nd Edition , Item 28: Use comodines delimitados para aumentar la flexibilidad de la API
- "PECS significa productor-
extends
, consumidor-super
- "PECS significa productor-
Preguntas relacionadas
- Demasiados para listar, PECS,
new Integer(0)
vsvalueOf
, etc.
No lo entendí por un tiempo. Muchas de las respuestas aquí y otras preguntas muestran específicamente cuándo y dónde ciertos usos son errores, pero no tanto por qué.
Así es como finalmente lo conseguí. Si tengo una función que agrega Number
s a una List
, me gustaría agregarlos de tipo MySuperEfficientNumber
que es mi propia clase personalizada que implementa Number
(pero no es una subclase de Integer
). Ahora es posible que la persona que llama no sepa nada sobre MySuperEfficientNumber
, pero siempre que sepa que trata los elementos agregados a la lista como nada más específico que Number
, estarán bien.
Si declaro mi método como:
public static void addNumbersToList(List<? extends Number> numbers)
Entonces la persona que llama podría pasar en una List<Integer>
. Si mi método agregara un MySuperEfficientNumber
al final de los numbers
, la persona que llama ya no tendría una List
de Integer
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 método addNumbersToList
. Obtendrás algo como:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Porque los numbers
pueden ser cualquier tipo específico de Number
, no necesariamente algo con lo que MySuperEfficientNumber
es compatible. Si volteaba la declaración para usar super
, el método se compilaría sin error, 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>)
Debido a que mi método dice: "No crea que su List
puede ser más específica que el Number
. Podría agregar todo tipo de Number
extraños a la lista, solo tendrá que lidiar con eso. pensar en ellos como algo incluso más general que Number
- like 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
me proporciones, siempre que cada elemento sea al menos un Number
. Puede ser cualquier tipo de Number
, incluso tu propio Number
extraño, personalizado y confeccionado s. Mientras implementen esa interfaz, estamos bien. No voy a agregar nada a su lista, ya que no sé qué tipo de hormigón real está utilizando allí ".
Para la primera parte, la List<Number>
cabe en la List<? super Number>
List<? super Number>
pero no puede agregar un Object
a una List<Number>
. Es por eso que no puede agregar un Object
a la List<? super Number>
List<? super Number>
.
Por otro lado, puede agregar todas las subclases de Number
( Number
incluido) a su lista.
Para la segunda parte, String
es un Object
, pero String
no es una superclase de Number
.
Si funcionara así, ya que cada clase es una subclase de Object
, super
no tendría ningún significado.
Veamos todos los casos posibles con List<? super Number>
List<? super Number>
:
- La lista pasada es una
List<Object>
-
List<Object>
funcionará -
Object
ajusta en<? super Number>
<? super Number>
- Puede agregar cualquier subtipo de
Number
a unaList<Object>
- Incluso si también pudiera agregar
String
lo único de lo que está seguro es que puede agregar cualquier subclase deNumber
.
-
- La lista aprobada es una
List<Number>
:-
List<Number>
funcionará -
Number
cabe en<? super Number>
<? super Number>
- Puede agregar cualquier subtipo de
Number
a unaList<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 unNumber
, no podría agregar ninguna subclase deNumber
en unaList<Integer>
(por ejemplo, unFloat
) -
super
no significa una subclase.
-
- La lista pasada es una
List<String>
(o cualquier clase que no se extiendeNumber
ni en la "súper jerarquía" deNumber
(es decir,Number
yObject
):-
List<String>
no funcionará -
String
no cabe en elNumber
"súper jerarquía" - Incluso si
String
encaja enObject
(que es una superclase deNumber
) no estarás seguro de poder agregar unNumber
a unaList
que contenga cualquier subclase de una de las superclases deNumber
) -
super
no significa ninguna subclase de una de las súper clases, solo significa una de las súper clases.
-
Como funciona ?
Podría decir que siempre que pueda agregar cualquier subclase de Number
con su List
tipeada, respetará la palabra clave super
.
Yo tuve el mismo problema. Creo que la sintaxis es la causa de la confusión.
List<? super Number>
List<? super Number>
parece sugerir " una lista de cosas que son supertipos de Número ", pero lo que realmente significa es " una lista de cosas que tienen Número como su supertipo ".
Una vez que comencé a leerlo, hizo clic.
List<? super Number>
List<? super Number>
es una List<AncestorOfNumber>
donde podemos convertir implícitamente cada Number
a su tipo super AncestorOfNumber
.
Considere esto: ¿Qué tipo genérico necesita ser ????
en el siguiente ejemplo?
InputStream mystream = ...;
void addTo(List<????> lsb) {
lsb.add(new BufferedInputStream(mystream));
}
List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
...
{ addTo(lb); addTo(li); addTo(lo); }
La respuesta: ????
es algo a lo que podemos lanzar BufferedInputStream
, que es ese mismo o uno de sus antepasados ? super BufferedInputStream
? super BufferedInputStream
List<? super Number>
List<? super Number>
significa que el tipo de referencia de la variable sugiere que tenemos una lista de números, objetos o serializables.
La razón por la que no puede agregar un Objeto, es porque el Compilador no sabe CUÁLES de estas clases están en la definición genérica del objeto instanciado real, por lo que solo le permite pasar Número o subtipos de Número, como Doble, Entero y pronto.
Digamos que tenemos un método que devuelve una List<? super Number>
List<? super Number>
. La creación del objeto dentro del método está encapsulada desde nuestra vista, simplemente no podemos decir si es algo como esto:
List<? super Number> returnValue = new LinkedList<Object>();
o
List<? super Number> returnValue = new ArrayList<Number>();
Entonces, el tipo genérico podría ser Objeto o Número. En ambos casos, podríamos agregar Number, pero solo en un caso podríamos agregar Object.
Debe distinguir entre el tipo de referencia y el tipo de objeto real en esta situación.