Saltar a contenido

Abstracción

La abstracción es un proceso de ocultar los detalles de implementación y mostrar solo la funcionalidad al usuario.

De otra manera, muestra solo las cosas esenciales para el usuario y oculta los detalles internos.

Una clase abstracta es una clase que no se puede instanciar pero que puede ser el padre de otras clases. Esto es útil, por ejemplo, cuando tenemos un concepto abstracto o amplio como Vehículo o Animal pero en realidad los objetos reales serían tipos específicos como Coche, Avión, Perro, etc.

Aunque no se puede instanciar, una clase abstracta define métodos y variables que heredan las clases hijas.

Cómo crear una clase abstracta

Para crear una clase abstracta en IntelliJ, haremos lo siguiente:

  1. Botón derecho en el paquete de nuestra aplicación → New ---> Java class y añadimos la palabra reservada abstract:
public abstract class Animal {
}

Métodos abstractos

Las clases abstractas pueden opcionalmente contener métodos abstractos. También pueden contener métodos no abstractos, que serán heredados por los hijos.

Un método abstracto no tiene cuerpo. (No tiene código). Solo se escribe la signatura del método con la palabra reservada abstract.

Vamos a crear una clase abstracta Animal:

public abstract class Animal {

    // Campo (variable de instancia)
    protected String nombre;

    // Constructor
    public Animal(String nombre) {
        this.nombre = nombre;
    }

    // Métodos abstractos (deben ser implementados por las subclases)
    public abstract void alimentar();

    public abstract void mover();

    // Método concreto (ya implementado en la clase base)
    public String getNombre() {
        return nombre;
    }
    //  Método Concreto
    public void descansar() {
        System.out.println(nombre + " está tomando una siesta ligera.");
    }
}

Si ahora creamos una clase hija que herede de Animal obtendremos un error:

abstracción

Es necesario implementar los métodos abstractos para la clase derivada. la clase puede implementar sus propios métodos.

public class Dog extends Animal {

    // Constructor que llama al constructor de la clase base (Animal)
    public Dog(String nombre) {
        super(nombre);
    }

    // Implementación obligatoria del método abstracto
    @Override
    public void alimentar() {
        System.out.println("El perro " + getNombre() + " está comiendo.");
    }

    // Implementación obligatoria del método abstracto
    @Override
    public void mover() {
        System.out.println("Se está moviendo ruidosamente");
    }
    //método propio de Dog
    public void darLaPata(){
        System.out.println("Ofrece la pata amablemente");
    }
}
Otro ejemplo de clase derivada
public class Gato extends Animal {

    // Constructor: Llama al constructor de la clase base Animal
    public Gato(String nombre) {
        super(nombre); // Llama a public Animal(String nombre)
    }

    // 1. Implementación del método abstracto alimentar()
    @Override
    public void alimentar() {
        System.out.println("El gato " + getNombre() + " está bebiendo leche y come pescado.");
    }

    // 2. Implementación del método abstracto mover()
    @Override
    public void mover() {
        System.out.println("Se está moviendo silenciosamente.");
    }

    // 3. Método específico de la subclase Gato
    public void maullar() {
        System.out.println(getNombre() + " dice: ¡Miau! Miau!");
    }
}
Ejemplo de instancias

public class Main {
    public static void main(String[] args) {

        // --- Gato ---
        Gato misifu = new Gato("Misifu"); 

        System.out.println("--- Llamando a Gato ---");
        // Se llama al método sobreescrito de la subclase Gato
        misifu.alimentar();
        misifu.mover();
        //método propio
        misifu.maullar();
        //llamada a método común implementado en Animal
        misifu.descansar();

        // --- Perro ---

        Dog max = new Dog("Max");

        System.out.println("\n--- Llamando a Perro ---");
        // Se llama al método sobreescrito de la subclase Dog
        max.alimentar();
        max.mover();
        //método propio
        max.darLaPata();
        //llamada a método común implementado en Animal
        max.descansar();
    }
}

Clases hijas abstractas

Un hijo abstracto de un padre abstracto no tiene que definir métodos no abstractos para los métodos abstractas que hereda. Esto significa que puede haber varios pasos entre una clase base abstracta y una clase secundaria que no es completamente abstracta.

Por ejemplo una subespecie Ave puede ser abstracta con nuevos métodos abstractos

public abstract class Ave extends Animal {
    private

    // Constructor
    public Ave(String nombre) {
        super(nombre);
    }

    // Nuevo método abstracto específico de la subclase Ave
    public abstract void volar();
}
La clase que herede de Ave tiene que implementar tanto los métodos de Animal como de Ave

public class Ganso extends Ave{
    public Ganso(String nombre) {
        super(nombre);
    }
    //Implementa todos los métodos abstractos heredados
    @Override
    public void volar() {
        System.out.println("Vuela lento pero mucha distancia");
    }

    @Override
    public void alimentar() {
        System.out.println("Come insectos en la laguna");

    }

    @Override
    public void mover() {
        System.out.println("Se desplaza elegantemente por el agua y cómicamente por tierra");
    }
}

Resumen de característica de las clase abstractas

Característica Descripción Ejemplo (Animal / Gato)
Declaración Se usa la palabra clave abstract delante de class. public abstract class Animal { ... }
Instanciación No se pueden crear objetos directamente de esta clase. No puedes hacer Animal a = new Animal("Pepe");.
Métodos Abstractos No tienen cuerpo (solo firma). Obligan a las subclases a implementarlos. public abstract void alimentar(); (En Animal)
Métodos Concretos Tienen cuerpo y lógica. Son heredados y compartidos por las subclases. public void descansar() { ... } (En Animal)
Herencia Una subclase concreta debe implementar todos los métodos abstractos del padre. class Gato extends Animal { @Override public void alimentar() {...} }
Constructores Pueden tener constructores, pero solo son llamados por la subclase usando super(). El constructor de Gato llama a super(nombre); del constructor de Animal.