Autor Tema: Java signaturas parametrizadas signo ? diferencia entre super y extends CU00915C  (Leído 4317 veces)

RaGa

  • Moderador Global
  • Intermedio
  • *******
  • APR2.COM
  • Mensajes: 234
    • Ver Perfil
Mi consulta es sobre cómo debo leer (interpretar) la información del API.
En este caso analicemos los dos métodos utilizados en el ejemplo resuelto, los cuales  pertenecen a la clase Collections.

Analicemos el siguiente método:

static <T extends Comparable<? super T>> void       sort(List<T> list)

Lo que entiendo de este método es que:
el método es un método estático tipo procedimiento (no devuelve nada).
Es aplicable sobre objetos tipo T, los que implementan la interface Comparable (o heredan una clase que la implemente).
Y que a su vez esa interface Comparable está definida sobre objetos que heredan la clase T.
El parámetro que se debe pasar al método es un objeto tipo List definido sobre objetos tipo T.

¿ Esto es correcto ?

Analizando la información del API de este otro método:

static <T> void              sort(List<T> list, Comparator<? super T> c)

entiendo que: es una método estático tipo procedimiento también.
Los parámetros que se deben pasar al método son dos:
El primero, un objeto tipo List definido sobre objetos tipo T.
El segundo, un objeto tipo Comparator definido sobre objetos que hereden la clase T.

¿ Esto es correcto ?
« Última modificación: 25 de Mayo 2015, 19:58 por Alex Rodríguez »

Alex Rodríguez

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 2050
    • Ver Perfil
Buenos días es interesante lo planteado y un tanto complejo, creo que pocas personas dominan realmente esto (yo tampoco)


El interrogante ? vendría a significar "cualquier clase que..."

List<Persona> se refiere a una colección de objetos Persona

List<? extends Persona> se refiere a una colección de objetos que heredan de Persona, es decir, la clase Persona y todas sus subclases. Si definimos List<? extends Persona> miLista; entonces miLista sólo puede contener objetos Persona o que hereden de Persona.

Si tenemos public class Bombero extends Persona y public class Perro extends Animal a un método que pida List<? extends Persona> le podemos pasar una lista de bomberos, pero no una lista de perros.

List<? super Persona> se refiere a una colección de objetos que son superclase de Persona, es decir, la clase Persona y todas sus superclases (hasta Object).

Yo interpretaría static <T extends Comparable<? super T>> void       sort(List<T> list) como que para ordenar una lista esta lista debe contener objetos T que implementen comparable sobre alguna de sus superclases.

static <T> void              sort(List<T> list, Comparator<? super T> c) sería que para ordenar una lista usando un Comparator de la clase o de una de sus superclases.

Quizás una forma de aclararse sea ir haciendo pruebas con código y viendo los resultados porque explicarlo con descripciones es complicado y posiblemente nos equivoquemos simplemente mezclando términos.

Esta explicación en inglés es interesante:

Citar

 Java type parameters are used as type placeholders.
   
public class List<X>  { }

A List<X> is a container for X objects, and X can be instantiated with any class: you can have List<Object>, List<String>, and List<Number>.


Introducing bounds: extends

You often want to restrict the set of types that can be used in instantiation. If you create a class Garage, you want it to hold only Vehicle objects.

The syntax you use in Java is like this:
   
public class Garage<X extends Vehicle> { }
Every time you instantiate the Garage, the type parameter has to be a subclass of Vehicle.

class Car extends Vehicle { }
class Motorcycle extends Vehicle { }
class Fruit extends Object { }
Thus Garage<Car> and Garage<Motorcycle> are OK but Garage<Fruit> is not OK.

You can specify more than one bound with extends:

   
class Vehicle { }
interface PassengerVehicle { }
interface MotorVehicle { }
class ParkingGarage<X extends Vehicle & MotorVehicle & PassengerVehicle>
You can specify at most one class in the bound (obviously, as you can only inherit from one class in Java) and as many interfaces as you want.

The bound can refer to the typing parameter:
   
class BST<X extends Comparable<X>> {}
BST class can only be instantiated with classes X which implement the Comparable<X> interface.

Bounds in method parameters

Java method can be parametrized, too. The syntax is as follows:
   
<T> T getRandomElement(List<T> list) {}
As with class definitions, you often want to restrict the type parameter in the method. A method which takes a list of Vehicles and returns the fastest vehicle in the list can have the following type.

   
<T extends Vehicle> T getFastest(List<T> list) { }
You can pass as argument a list of any vehicles. List<Car> is OK, List<Motorcycle> is OK, List<Vehicle> is OK, too. List<Number> is not OK.

Note that the following declaration wouldn't do the trick.

   
Vehicle getFastest2(List<Vehicle> list) { }
The argument to the method getFastest2 has to be exactly a List<Vehicle>, and not a List<Car>, because List<Car> is not a subtype of List<Vehicle>,

Wilcards

Take a look at the following declaration.

   
<T extends Vehicle> int totalFuel(List<T> list) { }
The parameter T occurs only once in the method signature, in an argument. You can imagine that the method body does not use the name T either. In this case you can use an alternative syntax, called wildcards, denoted with ?:

   
int totalFuel(List<? extends Vehicle> list) { }
The two signatures for totalFuel are equivalent. The meaning of <? extends Vehicle> is: I don't care what the type parameter is, as long as it is a subclass of Vehicle.

Introducing bounds: super

There is also dual bound, called super. As you guess it is used to denote that you can pass only superclasses of the bound. There are some differences between extends and super, though.
You can't use super in class declaration

The super bound is not allowed in class definition.

   
//this code does not compile !
class Forbidden<X super Vehicle> { }
Why? Because such construction doesn't make sense. For example, you can't erase the type parameter with Vehicle because the class Forbidden could be instantiated with Object. So you have to erase type parameters to Object anyway. If think about class Forbidden<Object>, it can take any value in place of X, not only superclasses of Vehicle. There's no point in using super bound, it wouldn't get us anything. Thus it is not allowed.

Wildcards

The syntax for wildcards is also similar to extends:
   
int totalValue(Valuer<? super Vehicle> valuer)
The method has to take a comparator which is able to compare Vehicles. If it compares Objects as well, that's fine too.

When to use extends and super

Wildcards are most useful in method parameters. They allow for the necessary flexibility in method interfaces.

People are often confused when to use extends and when to use super bounds. The rule of thumb is the get-put principle. If you get something from a parametrized container, use extends.

   
int totalFuel(List<? extends Vehicle> list) {
    int total = 0;
    for(Vehicle v : list) {
        total += v.getFuel();
    }
    return total;
}
The method totalFuel gets Vehicles from the list, asks them about how much fuel they have, and computes the total.

If you put objects into a parametrized container, use super.
   
int totalValue(Valuer<? super Vehicle> valuer) {
    int total = 0;
    for(Vehicle v : vehicles) {
        total += valuer.evaluate(v);
    }
    return total;
}
The method totalValue puts Vehicles into the Valuer.

It's useful to know that extends bound is much more common than super.

One more tip: if you are intimidated by wildcards (which is natural in the beginning), try to write the explicitly parametrized version first. In typical usage the two versions are equivalent. Eventually, you'll figure out when you can get rid of type parameters and use wildcards.


RaGa

  • Moderador Global
  • Intermedio
  • *******
  • APR2.COM
  • Mensajes: 234
    • Ver Perfil
Muy buena tu explicación Alex. Hacía un tiempo que venía viendo en el API de Java estos signos "?" y no sabía de qué se trataría.

De más está decir que ese texto que transcribiste no tiene ni una coma de desperdicio! Magistralmente esclarecedor. Su lectura -como suele pasar- me llevó a investigar otros temas, en este caso: tipos y métodos genéricos.
Y como siempre, escribir y probar código para poner las cosas en práctica con distintas variantes. Pienso es la mejor forma de ir madurando e incorporando los conceptos.

Muchas gracias Alex, como siempre muy interesantes tus aportes!  :)

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".