TutorJava Nivel Básico: tutorial completo Java 21

La herencia permite crear nuevas clases a partir de clases existentes, reutilizando y especializando el comportamiento. El polimorfismo permite tratar objetos de distintas clases de forma uniforme a través de una interfaz o clase base común.

Herencia con extends

En Java, la herencia es simple: una clase solo puede extender una clase padre directa. La clase hija hereda todos los miembros no privados del padre:

public class Animal {
    protected String nombre;

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

    public void hacerSonido() {
        System.out.println(nombre + " hace un sonido.");
    }

    public String getNombre() { return nombre; }
}

public class Perro extends Animal {
    private String raza;

    public Perro(String nombre, String raza) {
        super(nombre);   // llama al constructor del padre
        this.raza = raza;
    }

    @Override
    public void hacerSonido() {
        System.out.println(nombre + " ladra: ¡Guau!");
    }

    public String getRaza() { return raza; }
}

public class Gato extends Animal {
    public Gato(String nombre) { super(nombre); }

    @Override
    public void hacerSonido() {
        System.out.println(nombre + " maúlla: ¡Miau!");
    }
}

super: llamar al padre

super referencia a la clase padre. Se usa en el constructor hijo (super(...) debe ser la primera línea) o para llamar a métodos del padre que han sido sobreescritos.

@Override

La anotación @Override le dice al compilador que ese método sobreescribe uno del padre. Si te equivocas en la firma (nombre o parámetros), el compilador te avisa. Ponla siempre que sobreescribas.

Polimorfismo

El polimorfismo permite referenciar objetos de distintas clases hijas con el tipo de la clase padre. En tiempo de ejecución, Java invoca el método de la clase real del objeto (despacho dinámico):

Animal[] animales = {
    new Perro("Rex", "Pastor Alemán"),
    new Gato("Misi"),
    new Perro("Toby", "Labrador")
};

for (Animal a : animales) {
    a.hacerSonido();  // invoca el método correcto según el tipo real
}
// Rex ladra: ¡Guau!
// Misi maúlla: ¡Miau!
// Toby ladra: ¡Guau!

instanceof y pattern matching (Java 16+)

Para comprobar el tipo real de un objeto y hacer casting, Java 16 introdujo pattern matching para instanceof, que evita el cast explícito:

// Antes de Java 16 (verboso)
if (animal instanceof Perro) {
    Perro p = (Perro) animal;
    System.out.println(p.getRaza());
}

// Desde Java 16 (conciso)
if (animal instanceof Perro p) {
    System.out.println(p.getRaza());
}

La clase Object

Todas las clases Java extienden implícitamente Object, la raíz de la jerarquía. Los métodos más importantes que hereda toda clase y que deberías sobreescribir cuando corresponda:

  • toString(): representación textual del objeto. Lo usa System.out.println.
  • equals(Object o): igualdad lógica (por contenido, no por referencia).
  • hashCode(): entero para uso en HashMap/HashSet. Regla: si dos objetos son equals, deben tener el mismo hashCode.
public class Producto {
    private String codigo;
    private String nombre;

    // Constructor, getters...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Producto p)) return false;
        return codigo.equals(p.codigo);
    }

    @Override
    public int hashCode() {
        return codigo.hashCode();
    }

    @Override
    public String toString() {
        return "Producto{codigo=" + codigo + ", nombre=" + nombre + "}";
    }
}

final: clases y métodos no extensibles

Una clase final no puede ser extendida (ejemplo: String en Java es final). Un método final no puede ser sobreescrito por las clases hijas. Úsalo cuando el comportamiento debe ser invariante.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP