I contenitori (elenchi, ecc.) che sono definiti per contenere oggetti di una determinata classe base possono contenere qualsiasi oggetto esteso da quella classe base. Ci sono molte situazioni nella progettazione del software in cui questa funzionalità è utile. La classe base può specificare che alcuni comportamenti saranno richiesti, ma potremmo non sapere esattamente come dovrebbero essere implementati perché sarà diverso a seconda delle caratteristiche delle nuove classi che si estendono da esso.
Per prendere a prestito da un esempio molto comune di polimorfismo, in un programma di disegno, dì che hai tre classi:
abstract class Shape {
private Point position;
public Point getPosition(){ return position; }
// Every Shape has an area, but we don't know
// how to calculate it until we know what kind
// of Shape it is. So this is defined, but it's
// abstract.
public abstract float getArea();
}
class Rectangle extends Shape {
private float length;
private float width;
public float getArea(){ return length * width; }
public float getLength(){ return length; }
public float getWidth(){ return width; }
}
class Circle extends Shape {
private float radius;
public float getArea(){ return Math.PI * radius * radius; }
public float getRadius(){ return radius; }
}
Ora, nel tuo codice, potresti avere un contenitore che contiene% oggettiShape
. Potrebbero essere Circle
o Rectangle
oggetti. Definendo il tuo contenitore in termini di classe base, puoi ora tenere entrambe le sottoclassi in modo sicuro e puoi chiamare getPosition () e getArea () su qualsiasi cosa nel contenitore.
List<Shape> shapes = new ArrayList<Shape>();
shapes.add(new Rectangle(5,10));
shapes.add(new Circle(5));
for (Shape s : shapes){
s.getPosition(); // <-- this is OK
s.getArea(); // <-- this is OK
s.getRadius(); // <-- This produces a compiler error
s.getWidth(); // <-- This produces a compiler error
}
Chiamare getRadius () o getWidth () su Shape
causa un errore del compilatore, poiché Shape
non fornisce tali metodi. Dovresti saperne di più sull'oggetto e lanciare l'oggetto sul tipo corretto prima di chiamare i metodi più specifici.
if (s instanceof Rectangle){
((Rectangle)s).getWidth();
}
if (s instanceof Circle){
((Circle)s).getRadius();
}
Si noti che questo esempio utilizza in modo specifico una classe base astratta con ereditarietà, poiché esiste qualcosa che la classe base può effettivamente implementare (la posizione). Nel caso in cui la classe base sia puramente astratta e non fornisca alcuna implementazione concreta da ereditare dalle sottoclassi, probabilmente si definirà un'interfaccia, piuttosto che una classe. E le sottoclassi implementerebbero le interfacce.