Menu
Coddy logo textTech

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 Circle

Las 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.

challenge icon

Desafío

Fácil

Construyamos 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 campo brand (String) y un campo wattage (int). Incluye un constructor que inicialice ambos campos, métodos getter para cada uno, y un método regular getInfo() que devuelva: [brand] - [wattage]W. Define un método abstracto operate() 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 adicional capacity (int) que representa el tamaño de la carga en kg. Usa super para manejar la inicialización del padre. Implementa el método operate() para imprimir: [brand] washing machine is washing [capacity]kg of clothes
  • Microwave.java: Crea otra clase concreta que extienda de Appliance. Los microondas tienen un campo power (int) que representa el porcentaje del nivel de potencia. Implementa operate() para imprimir: [brand] microwave is heating at [power]% power
  • Main.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 tipo Appliance. Recorre el arreglo y, para cada electrodoméstico, imprime su información usando getInfo() seguido de la llamada a operate().

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 implementation

Las 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()
    }
}
quiz iconPonte a prueba

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