Clases abstractas
En resumen: Una clase abstracta es una clase padre de la que no puedes crear objetos; sus métodos abstractos no tienen cuerpo, y cada clase hija tiene que escribirlos.
Problema
Cero metros cuadrados
Estás escribiendo un programa que pide baldosas para una terraza. La terraza tiene tres partes: un rectángulo de 4 × 3 m, una parte redonda con un radio de 1 m y un triángulo con una base de 2 m y una altura de 3 m. Hay una clase padre Shape con area(), que devuelve 0, y cada figura lo sobrescribe.
El colega que escribió Triangle se olvidó:
class Triangle extends Shape { private double base; private double height;
public Triangle(double base, double height) { this.base = base; this.height = height; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;for (Shape s : terrace) { total += s.area();}System.out.printf("Baldosas: %.2f m²%n", total);Baldosas: 15.14 m²Necesitas 18,14 m². El programa no da ningún error, y al pedido le faltan 3 m². El cero de la clase padre ha escondido el método que falta.
¿Y qué es new Shape()? Una figura sin forma. Un objeto así no tiene ninguna razón para existir.
La idea
Clase abstracta: una promesa que cumplen las clases hijas
Shape no es una figura real. Lo único que dice es «toda figura tiene un área». Así que hazla abstracta:
abstract class Shape { public abstract double area();}- Un método abstracto no tiene cuerpo, solo una cabecera y
;. No puedes dar un área razonable a «una figura en general», así que no das ninguna. - Una clase abstracta no puede tener objetos:
new Shape()no compila. - Cada clase hija tiene que escribir el método. Si no, el programa no compila.
Ahora el Triangle olvidado da un error al compilar:
"Triangle is not abstract and does not override abstract method area() in Shape".
Un error que detecta el compilador es mejor que un error en el pedido.Más precisamente, para curiosos
- Una clase hija que no escribe el método abstracto también puede ser abstracta. Entonces la promesa pasa a sus propias clases hijas.
- Una clase puede ser abstracta sin un solo método abstracto. Así solo prohíbes los objetos de ella.
Míralo
El triángulo cumple la promesa
Añade el método a Triangle:
class Triangle extends Shape { // ... base, height y el constructor
@Override public double area() { return base * height / 2; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;for (Shape s : terrace) { total += s.area();}System.out.printf("Baldosas: %.2f m²%n", total);Baldosas: 18.14 m²El bucle es el mismo. El array Shape[] guarda figuras de tres clases, y s.area() llama al método de cada una. El polimorfismo de la lección anterior también funciona con una clase padre abstracta.
Pruébalo
Encuentra el error
Elige la línea con el error.
new Shape() es un intento de crear un objeto de una clase abstracta. La primera línea está bien: new Shape[3] crea un array de tres casillas vacías para figuras, no figuras. En ellas puedes poner un rectángulo, un círculo o un triángulo.
La idea
Abstracta, pero no vacía
Una clase abstracta puede tener todo lo que tiene una normal: campos, un constructor, métodos normales. Haz que cada figura tenga un nombre y un método que la describa:
abstract class Shape { private String name;
public Shape(String name) { this.name = name; }
public abstract double area();
public String describe() { return name + ": " + String.format("%.2f", area()) + " m²"; }}describe() se escribe una vez, en Shape. Llama a area(), que Shape todavía no tiene. No es un problema: describe() siempre se llama sobre una figura real, y una figura real tiene un área.
De Shape no creas objetos, pero su constructor es útil: lo llaman las clases hijas.
class Circle extends Shape { private double radius;
public Circle(double radius) { super("círculo"); this.radius = radius; } // ... area() como antes}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };for (Shape s : terrace) { System.out.println(s.describe());}rectángulo: 12.00 m²círculo: 3.14 m²triángulo: 3.00 m²La clase padre escribe lo común (el nombre, la descripción), y las clases hijas escriben lo distinto (el área).
public abstract double area();
public String describe() { return name + ": " + String.format("%.2f", area()) + " m²";}
Shape s = new Circle(1);System.out.println(s.describe());Paso a paso, en palabras
- Shape promete area(), pero no tiene cuerpo. No puede haber un objeto que sea solo un Shape.
- El objeto es un círculo: la parte Circle tiene su propio area(). Cumple la promesa.
- s.describe() está escrito en Shape y sirve para toda figura.
- Llama a area(). El objeto es un círculo, así que se ejecuta el método de Circle: π · 1 · 1.
- describe() arma el texto. Lo común está en la clase padre, lo distinto en la clase hija.
- La salida es el área del círculo, con dos cifras después del punto decimal.
Pruébalo
¿De quién es el método?
¿Qué imprimirá el programa?
// Shape y Circle son de la tarjeta de arriba
Shape s = new Circle(2);System.out.println(s.describe());La variable es del tipo abstracto Shape, y eso está permitido. El objeto es un círculo con un radio de 2. describe() pertenece a Shape, pero el area() de dentro es el del círculo: π · 2 · 2 ≈ 12,57.
Pruébalo
¿Qué falta?
¿Qué tiene que ir en el hueco para que no puedas crear un objeto de Shape?
class Shape {
public abstract double area();
}abstract. En cuanto una clase tiene un método abstracto, la propia clase tiene que ser abstracta. Si no, el programa no compila.Ejercicio
Figuras y animales
Resuelve las tareas en tu editor.
1. Área y perímetro. Una clase abstracta Shape con un nombre y dos métodos abstractos: area() y perimeter(). Clases hijas Circle (radio) y Rectangle (ancho y alto). Pon un círculo con un radio de 5 y un rectángulo de 4 × 2,5 en un array e imprime los dos valores con dos cifras después del punto decimal.
Círculo: área 78.54, perímetro 31.42Rectángulo: área 10.00, perímetro 13.002. Animales. Una clase abstracta Animal con un método abstracto sound() y un método normal breathe() que imprime «El animal respira.». Clases hijas Cat y Dog. Llama al sonido de los dos en un bucle, y después a breathe() del primero.
¡Miau, miau!¡Guau, guau!El animal respira.Ejercicio
Vehículos y actuaciones
3. Vehículos. Una clase abstracta Vehicle con un método abstracto move(). SimpleCar y Bicycle heredan de ella. Bicycle tiene además su propio método pedal().
El coche arrancaLa bicicleta arrancaPedaleogarage[1].pedal(). ¿Por qué no compila, aunque ahí hay una bicicleta?4. Actuaciones. Una clase abstracta Performance con un título y un método abstracto start(). DanceMove y Song heredan de ella. Song tiene además un método sing().
Empieza el baile moonwalkEmpieza la canción 'Bohemian Rhapsody'Canto 'Bohemian Rhapsody'Más tareas
- Triángulo. Añade a la tarea 1 un
Trianglecon sus tres lados. El perímetro es su suma, y el área sale de la fórmula de Herón: s = perímetro / 2, área = √(s(s − a)(s − b)(s − c)). Para los lados 3, 4 y 5 el área es 6.00 y el perímetro es 12.00. - Empleado abstracto. Haz abstracta la clase
Employeede la lección anterior, con unmonthlyPay()abstracto. AñadeFullTime(el salario se paga completo) y conservaManagereIntern. ¿Qué gana el programa? ¿Qué pierde?
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Métodos de instancia · Encapsulamiento · Herencia · Polimorfismo
- Continúa en: Interfaces · Comprobaciones de tipo · Principios de la POO · Lanzar excepciones y crear las tuyas
- Ejercicios sobre el tema: Herencia y polimorfismo