Clases y métodos abstractos
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 32 de 87.
A veces quieres definir una clase que sirva como un modelo pero que nunca deba ser instanciada directamente. Por ejemplo, una clase Shape genérica tiene sentido como clase padre, pero ¿cómo se vería una "forma" sin una forma específica? Aquí es donde entran las clases abstractas.
Una clase abstracta se declara con la palabra clave abstract y no puede ser instanciada. Puede contener tanto métodos regulares con implementaciones como métodos abstractos que no tienen cuerpo:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// Método abstracto - sin implementación
public abstract double getArea();
// Método regular - tiene implementación
public String getColor() {
return color;
}
}Los métodos abstractos declaran qué debe hacer una subclase, pero no cómo. Cualquier clase no abstracta que extienda una clase abstracta debe proporcionar implementaciones para todos los métodos abstractos:
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}Aún puedes usar clases abstractas como tipos de referencia, lo que permite el polimorfismo:
Shape shape = new Circle("red", 5.0);
System.out.println(shape.getArea()); // Llama a la implementación de CircleLas clases abstractas son ideales cuando las subclases comparten código común, pero cada una debe implementar ciertos comportamientos de manera diferente. Imponen un contrato al mismo tiempo que permiten la reutilización de código.
Desafío
FácilConstruyamos un sistema de electrodomésticos que demuestre el poder de las clases abstractas. Crearás una clase base Appliance que define lo que cada electrodoméstico debe hacer, permitiendo que los tipos específicos de electrodomésticos decidan exactamente cómo lo hacen.
Organizarás tu código en cuatro archivos:
Appliance.java: Crea una clase abstracta que sirva como plano para todos los electrodomésticos. Cada electrodoméstico tiene un campobrand(String) y un campowattage(int). Incluye un constructor que inicialice ambos campos, métodos getter para cada uno, y un método regulargetInfo()que devuelva:[brand] - [wattage]W. Define un método abstractooperate()que las subclases deben implementar; esto representa cómo cada electrodoméstico realiza su función principal.WashingMachine.java: Crea una clase concreta que extienda de Appliance. Las lavadoras tienen un campo adicionalcapacity(int) que representa el tamaño de la carga en kg. Usasuperpara manejar la inicialización del padre. Implementa el métodooperate()para imprimir:[brand] washing machine is washing [capacity]kg of clothesMicrowave.java: Crea otra clase concreta que extienda de Appliance. Los microondas tienen un campopower(int) que representa el porcentaje del nivel de potencia. Implementaoperate()para imprimir:[brand] microwave is heating at [power]% powerMain.java: Une todo utilizando polimorfismo. Recibirás cinco entradas: un nombre de marca, vataje, capacidad de la lavadora, otro nombre de marca y el nivel de potencia del microondas. Crea un WashingMachine y un Microwave, luego almacénalos en un arreglo de referencias de tipoAppliance. Recorre el arreglo y, para cada electrodoméstico, imprime su información usandogetInfo()seguido de la llamada aoperate().
Recibirás cinco entradas en este orden: primera marca (String), primer vataje (int), capacidad en kg (int), segunda marca (String) y nivel de potencia (int).
Tu salida debería mostrar cuatro líneas: para cada electrodoméstico, verás su línea de información seguida de su comportamiento de operación. ¡Observa cómo la clase abstracta te permite tratar diferentes electrodomésticos de manera uniforme mientras cada uno opera de su propia manera única!
Hoja de referencia
Una clase abstracta se declara con la palabra clave abstract y no puede ser instanciada directamente. Sirve como un plano para las subclases:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// Abstract method - no implementation
public abstract double getArea();
// Regular method - has implementation
public String getColor() {
return color;
}
}Los métodos abstractos no tienen cuerpo y deben ser implementados por subclases que no sean abstractas:
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}Las clases abstractas pueden utilizarse como tipos de referencia para el polimorfismo:
Shape shape = new Circle("red", 5.0);
System.out.println(shape.getArea()); // Calls Circle's implementationLas clases abstractas son ideales cuando las subclases comparten código común pero deben implementar ciertos comportamientos de manera diferente.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer entradas
String brand1 = scanner.nextLine();
int wattage1 = scanner.nextInt();
int capacity = scanner.nextInt();
scanner.nextLine(); // consumir nueva línea
String brand2 = scanner.nextLine();
int power = scanner.nextInt();
// TODO: Crear un objeto WashingMachine con brand1, wattage1 y capacity
// TODO: Crear un objeto Microwave con brand2, wattage1 (o el vataje apropiado) y power
// TODO: Crear un arreglo de referencias de Appliance y almacenar ambos objetos
// TODO: Recorrer el arreglo y para cada electrodoméstico:
// - Imprimir su información usando getInfo()
// - Llamar a operate()
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator