Supongamos que tengo esta jerarquía de clases ...
public abstract class Animal {
public abstract void eat();
public abstract void talk();
}
class Dog extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
class Cat extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
Y luego tengo ...
public static <T extends Animal> void addAnimal(T animal) {
animal.eat();
animal.talk();
}
public static void addAnimalPoly(Animal animal) {
animal.eat();
animal.talk();
}
¿Cuál es la diferencia cuando se usan parámetros de tipo acotado o polimorfismo?
¿Y cuándo usar uno u otro?
java
polymorphism
generics
HugoMelo
fuente
fuente
addAnimals(List<Animal>)
y agregar una Lista de gatos!Respuestas:
Estos dos ejemplos son equivalentes y, de hecho, se compilarán con el mismo código de bytes.
Hay dos formas en que agregar un tipo genérico acotado a un método como en su primer ejemplo hará cualquier cosa.
Pasar el parámetro de tipo a otro tipo
Estas dos firmas de métodos terminan siendo las mismas en el código de bytes, pero el compilador impone la seguridad de tipo:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
En el primer caso, solo se permite un
Collection
(o subtipo) deAnimal
. En el segundo caso, se permite unCollection
(o subtipo) con un tipo genéricoAnimal
o un subtipo.Por ejemplo, lo siguiente está permitido en el primer método pero no en el segundo:
La razón es que el segundo solo permite colecciones de animales, mientras que el primero permite colecciones de cualquier objeto que se pueda asignar a un animal (es decir, subtipos). Tenga en cuenta que si esta lista fuera una lista de animales que por casualidad contienen un gato, cualquiera de los métodos lo aceptaría: el problema es la especificación genérica de la colección, no lo que realmente contiene.
Devolviendo objetos
La otra vez que importa es con la devolución de objetos. Supongamos que existiera el siguiente método:
Podrías hacer lo siguiente con él:
Si bien este es un ejemplo artificial, hay casos en los que tiene sentido. Sin genéricos, el método tendría que regresar
Animal
y necesitaría agregar conversión de tipos para que funcione (que es lo que el compilador agrega al código de bytes de todos modos detrás de escena).fuente
Use genéricos en lugar de abatir. "Downcasting" es malo, pasando de un tipo más general a uno más específico:
... confías en que
a
es un gato, pero el compilador no puede garantizarlo. Podría resultar ser un perro en tiempo de ejecución.Aquí es donde usarías genéricos:
Ahora puede especificar que desea un cazador de gatos:
Ahora el compilador puede garantizar que hunterC solo capturará gatos, y hunterD solo capturará perros.
Entonces, solo use el polimorfismo regular si solo desea manejar clases específicas como su tipo base. La transmisión es algo bueno. Pero si se encuentra en una situación en la que necesita manejar clases específicas como su propio tipo, genéricamente, use genéricos.
O, realmente, si encuentra que tiene que abatir, use genéricos.
EDITAR: el caso más general es cuando desea posponer la decisión de qué tipos de tipos manejar. Entonces los tipos se convierten en un parámetro, así como los valores.
Digamos que quiero que mi clase de zoológico maneje gatos o esponjas. No tengo una superclase común. Pero aún puedo usar:
el grado en que lo bloquee depende de lo que esté tratando de hacer;)
fuente
Esta pregunta es antigua, pero un factor importante a considerar parece haberse omitido con respecto a cuándo usar el polimorfismo frente a los parámetros de tipo acotado. Este factor puede ser ligeramente tangente al ejemplo dado en la pregunta, pero creo que es muy relevante para el más general "¿Cuándo usar el polimorfismo frente a los parámetros de tipo acotado?"
TL; DR
Si alguna vez se encuentra moviendo el código de una subclase a una clase base en contra de su mejor juicio, debido a la imposibilidad de acceder a él de forma polimórfica, los parámetros de tipo acotado pueden ser una posible solución.
La respuesta completa
Los parámetros de tipo acotado pueden exponer métodos de subclase concretos no heredados para una variable miembro heredada. El polimorfismo no puede
Para elaborar ampliando su ejemplo:
Si se definió que la clase abstracta AnimalOwner tiene un
protected Animal pet;
y opta por el polimorfismo, el compilador generará un error en lapet.scratchBelly();
línea, diciéndole que este método no está definido para Animal.fuente
En su ejemplo, usted no (y no debe) usar el tipo acotado. Solo use parámetros de tipo acotado cuando sea necesario , ya que son más confusos de entender.
Aquí hay alguna situación en la que usará parámetros de tipo acotado:
Parámetros de colecciones
entonces puedes llamar
zoo.add(dogs)
no compilaría sin él<? extends Animal>
, porque los genéricos no son covariantes.Subclases
para limitar el tipo que puede proporcionar la subclase.
También puede usar varios límites
<T extends A1 & A2 & A3>
para asegurarse de que un tipo sea un subtipo de todos los tipos de la lista.fuente