Lecciones del módulo (2/2)
Polimorfismo y Clases Abstractas
El polimorfismo y las clases abstractas son conceptos avanzados de la programación orientada a objetos que permiten escribir código flexible, extensible e independiente de las implementaciones específicas.
El Polimorfismo
El término polimorfismo (muchas formas) se refiere a la capacidad de tratar objetos de diferentes clases como si pertenecieran a un tipo común. En Java, una variable de tipo superclase puede almacenar una referencia a un objeto de cualquier subclase.
class Animal {
public void makeSound() {
System.out.println("Verso...");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("Woof");
}
}
class Cat extends Animal {
public void makeSound() {
System.out.println("Miao");
}
}
Gracias al polimorfismo, podemos hacer esto:
Animal myAnimal1 = new Dog(); // Polimorfismo
Animal myAnimal2 = new Cat(); // Polimorfismo
myAnimal1.makeSound(); // Esegue il metodo di Dog (Woof)
myAnimal2.makeSound(); // Esegue il metodo di Cat (Miao)
La decisión de qué método invocar ocurre en tiempo de ejecución (Late Binding o dynamic dispatch) en función del objeto real y no del tipo de la variable de referencia.
Clases Abstractas (abstract)
Una clase abstracta es una clase marcada con la palabra clave abstract que no puede ser instanciada directamente (no puedes hacer new MyAbstractClass()). Sirve como un "modelo parcial" para otras clases.
Métodos Abstractos
Una clase abstracta puede contener métodos abstractos: métodos declarados sin cuerpo (sin llaves y sin código), que terminan con un punto y coma. Las subclases no abstractas están obligadas a implementar todos los métodos abstractos heredados.
abstract class Shape {
String color;
// Metodo astratto (senza corpo)
public abstract double getArea();
}
class Circle extends Shape {
double radius;
public Circle(double radius) {
this.radius = radius;
}
// Obbligatorio implementare getArea
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
Dynamic Binding / Late Binding
¿Cómo sabe Java qué método ejecutar en tiempo de ejecución? Este proceso se conoce como Late Binding (o Dynamic Binding). A diferencia de la sobrecarga (resuelta en tiempo de compilación), el compilador no sabe qué método específico se ejecutará. En su lugar, genera una instrucción de llamada genérica. En tiempo de ejecución, la Java Virtual Machine (JVM) examina el objeto real en memoria e invoca la implementación del método definida en la clase de ese objeto.
Clases Abstractas y Falta de Implementación
Si una subclase hereda de una clase abstracta, tiene dos opciones:
- Implementar todos los métodos abstractos: en este caso, la clase puede ser una clase normal (concreta) e instanciarse.
- Declararse abstracta: si la subclase no proporciona una implementación para todos los métodos abstractos heredados, debe declararse a su vez con la palabra clave
abstract.
abstract class Animal {
public abstract void makeSound();
}
// Questa classe DEVE essere astratta perché non implementa makeSound()
abstract class Canine extends Animal {
// Eredita makeSound() ma non lo implementa
}
Pruébalo tú
Declara una variable s de tipo Shape y asígnale un nuevo objeto Circle usando el polimorfismo.
Mostrar pista
Escribe `Shape s = new Circle();` para usar el tipo base como tipo del referencia.
Solución disponible después de 3 intentos
Haz que la clase Shape sea abstracta y añade el método abstracto double getArea(). Luego completa Square para que extienda Shape e implemente getArea() devolviendo side * side.
Mostrar pista
Declara `public abstract double getArea();` en Shape. En Square, añade `@Override public double getArea() { return side * side; }`.
Solución disponible después de 3 intentos
Declara en el main un arreglo de tipo Shape[] que contenga un objeto Circle con radio 2.0 y un objeto Square con lado 3.0. A continuación, usa un bucle (for o for-each) para imprimir en la consola el área de cada forma invocando el método getArea().
Mostrar pista
Declara el arreglo con `Shape[] shapes = { new Circle(2.0), new Square(3.0) };` y recórrelo con un bucle `for (Shape s : shapes)` imprimiendo `s.getArea()`.
Solución disponible después de 3 intentos