Súper palabra clave genérica de Java

Resuelto Denys Kniazhev-Support Ukraine asked hace 14 años • 7 respuestas

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 superla palabra clave:

  1. 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 Objectdebería encajar (ya que es el padre de Number) y Integerno debería. Por alguna razón ocurre lo contrario.

  1. 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?

Aceptado

El comodín acotado en List<? super Number>puede capturar Numbery 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 addcualquier supertipo de Numbera 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 Numberobjetos (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

Preguntas relacionadas

  • Demasiados para enumerarlos, PECS, new Integer(0)vs valueOf, etc.
polygenelubricants avatar Oct 02 '2010 20:10 polygenelubricants

La primera parte List<Number>encaja, List<? super Number>pero no puedes agregar un Objectarchivo List<Number>. Es por eso que no puedes agregar un Objecta List<? super Number>.

Por otro lado, puedes agregar todas las subclases de Number( Numberincluido) a tu lista.

Para la segunda parte, Stringes una Object, pero Stringno es una superclase de Number.

Si funcionara así, como cada clase es una subclase de Object, superno tendría significado.


Veamos todos los casos posibles con List<? super Number>:


  • La lista aprobada es unaList<Object>
    • List<Object>trabajará
    • Objectencaja<? super Number>
    • Puede agregar cualquier subtipo de Numbera unList<Object>
    • Incluso si también pudieras agregarlo, Stringlo único de lo que estás seguro es que puedes agregar cualquier subclase de Number.

  • La lista aprobada es una List<Number>:
    • List<Number>trabajará
    • Numberencaja<? super Number>
    • Puede agregar cualquier subtipo de Numbera unList<Number>

  • La lista pasada es una List<Integer>(o cualquier subclase de Number):
    • List<Integer>no funcionará
    • Integer es una subclase de Number, por lo que es exactamente lo que queremos evitar.
    • Incluso si un Integerencaja en a, Numberno podrá agregar ninguna subclase de Numberen a List<Integer>(por ejemplo, a Float)
    • superno significa una subclase.

  • La lista pasada es una List<String>(o cualquier clase que no se extienda Numberni esté en la "súper jerarquía" de Number(es decir, Numbery Object):
    • List<String>no funcionará
    • Stringno encaja en la Number"superjerarquía"
    • Incluso si Stringencaja Object(que es una superclase de Number), no estará seguro de poder agregar a Numbera Listque contenga cualquier subclase de una de las superclases de Number).
    • superno 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 Numbercon lo escrito List, respeta la superpalabra clave.

Colin Hebert avatar Oct 02 '2010 19:10 Colin Hebert

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 Numbers a a List, es posible que desee agregarlas del tipo MySuperEfficientNumberque 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 MySuperEfficientNumberal final de numbers, entonces la persona que llama ya no tendría a Listof Integers 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 addNumbersToListmétodo. Obtendrías algo como:

The method add... is not applicable for the arguments (MySuperEfficientNumber)

Porque numberspodría ser cualquier tipo específico Number, no necesariamente algo MySuperEfficientNumbercon 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 Listser de algo más específico que Number. Podría agregar todo tipo de Numbermensajes raros a la lista, pero tendrás que lidiar con ello. Si quieres pensar en ellos como algo incluso más general que Number... Objectestá bien, te garantizo que serán al menos Numbers, pero puedes tratarlos de manera más general si quieres".

Mientras que extendsdice: "Realmente no me importa qué tipo de Lists 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í".

onlynone avatar Sep 09 '2015 18:09 onlynone