Herencia y Polimorfismo en Java

JavaBeginner
Practicar Ahora

Introducción

En este laboratorio, exploraremos dos conceptos fundamentales de la programación orientada a objetos en Java: herencia y polimorfismo. Estas poderosas características nos permiten crear código más organizado, eficiente y flexible. Comenzaremos explorando la herencia, que nos permite crear nuevas clases basadas en las existentes, y luego pasaremos al polimorfismo, que nos permite tratar objetos de diferentes clases de manera uniforme.

Al final de este laboratorio, podrás:

  1. Crear jerarquías de clases utilizando herencia
  2. Sobrescribir métodos en subclases
  3. Comprender y utilizar el polimorfismo
  4. Implementar clases y métodos abstractos

No te preocupes si estos términos suenan complejos; desglosaremos todo en pasos simples y fáciles de seguir. ¡Comencemos nuestro emocionante viaje para mejorar tus habilidades de programación en Java!

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 94%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Creación de una Clase Base

Comenzaremos creando una clase base llamada Animal. Esta clase servirá como la base para nuestras otras clases.

  1. Abre tu terminal y navega hasta el directorio de tu proyecto:

    cd ~/project
  2. Crea un nuevo archivo llamado Animal.java utilizando el comando touch:

    touch Animal.java
  3. Abre Animal.java en tu editor de texto y agrega el siguiente código:

    public class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void eat() {
            System.out.println(name + " is eating.");
        }
    
        public void sleep() {
            System.out.println(name + " is sleeping.");
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    }

    Desglosemos este código:

    • Definimos una clase llamada Animal con dos atributos: name (una cadena de texto) y age (un entero).
    • La palabra clave private significa que estos atributos solo se pueden acceder dentro de la clase.
    • Tenemos un constructor que inicializa estos atributos cuando se crea un objeto Animal.
    • Tenemos dos métodos: eat() y sleep(), que imprimen lo que el animal está haciendo.
    • También tenemos métodos "getter" (getName() y getAge()) que nos permiten acceder a los atributos privados desde fuera de la clase.
  4. Guarda el archivo Animal.java.

Saving Animal java file
  1. Ahora, compilemos nuestra clase Animal para asegurarnos de que no haya errores. En tu terminal, ejecuta:

    javac Animal.java

    Si no hay mensajes de error, ¡tu clase se compiló correctamente!

Creando una Subclase

Ahora que tenemos nuestra clase base Animal, creemos una subclase llamada Dog. Esto demostrará cómo funciona la herencia en Java.

  1. En tu terminal, crea un nuevo archivo llamado Dog.java:

    touch Dog.java
  2. Abre Dog.java en tu editor de texto y agrega el siguiente código:

    public class Dog extends Animal {
        private String breed;
    
        public Dog(String name, int age, String breed) {
            super(name, age);  // Llama al constructor de la superclase
            this.breed = breed;
        }
    
        public String getBreed() {
            return breed;
        }
    }

    Desglosemos este nuevo código:

    • extends Animal le dice a Java que Dog es una subclase de Animal. Si bien Dog hereda de Animal, los atributos privados name y age en Animal no son directamente accesibles en Dog. Sin embargo, Dog puede acceder a estos atributos a través de los métodos getter públicos heredados getName() y getAge().
    • Hemos agregado un nuevo atributo breed que es específico de Dog.
    • El constructor toma tres parámetros. Utiliza super(name, age) para llamar al constructor de Animal, luego establece el breed.
    • Hemos agregado un nuevo método getBreed() que es específico de Dog.
  3. Guarda el archivo Dog.java.

  4. Compila la clase Dog:

    javac Dog.java

    Podrías ver una advertencia sobre Animal.class, pero está bien por ahora.

Demostración de la Herencia

Ahora que tenemos nuestras clases Animal y Dog, creemos un programa para demostrar cómo funciona la herencia.

  1. Crea un nuevo archivo llamado InheritanceDemo.java:

    touch InheritanceDemo.java
  2. Abre InheritanceDemo.java y agrega el siguiente código:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal genericAnimal = new Animal("Generic Animal", 5);
            Dog myDog = new Dog("Buddy", 3, "Labrador");
    
            System.out.println("Demonstrating Animal class:");
            genericAnimal.eat();
            genericAnimal.sleep();
    
            System.out.println("\nDemonstrating Dog class:");
            myDog.eat();  // Inherited from Animal
            myDog.sleep();  // Inherited from Animal
    
            System.out.println("\nDog details:");
            System.out.println("Name: " + myDog.getName());  // Inherited method
            System.out.println("Age: " + myDog.getAge());  // Inherited method
            System.out.println("Breed: " + myDog.getBreed());  // Dog-specific method
        }
    }

    Este programa crea instancias de las clases Animal y Dog y demuestra cómo la clase Dog hereda métodos de la clase Animal.

  3. Guarda el archivo InheritanceDemo.java.

  4. Compila y ejecuta el programa:

    javac InheritanceDemo.java
    java InheritanceDemo

    Deberías ver una salida similar a esta:

    Demonstrating Animal class:
    Generic Animal is eating.
    Generic Animal is sleeping.
    
    Demonstrating Dog class:
    Buddy is eating.
    Buddy is sleeping.
    
    Dog details:
    Name: Buddy
    Age: 3
    Breed: Labrador

Esta demostración muestra cómo la clase Dog hereda atributos y métodos de la clase Animal, mientras que también agrega su propio atributo específico (breed) y método (getBreed()).

Sobrescritura de Métodos

La sobrescritura de métodos es una característica que permite a una subclase proporcionar una implementación específica de un método que ya está definido en su superclase. Veamos cómo funciona esto.

  1. Abre Dog.java y agrega el siguiente método:

    @Override
    public void eat() {
        System.out.println(getName() + " is eating dog food.");
    }

    Agrega este método dentro de la clase Dog, pero fuera de cualquier otro método.

    La anotación @Override le dice al compilador que pretendemos sobrescribir un método de la superclase. No es obligatoria, pero es una buena práctica usarla.

  2. Guarda el archivo Dog.java.

  3. Ahora, modifiquemos nuestro InheritanceDemo.java para demostrar la sobrescritura de métodos. Abre InheritanceDemo.java y reemplaza su contenido con:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal genericAnimal = new Animal("Generic Animal", 5);
            Dog myDog = new Dog("Buddy", 3, "Labrador");
    
            System.out.println("Demonstrating method overriding:");
            genericAnimal.eat();
            myDog.eat();
    
            System.out.println("\nDemonstrating inherited method:");
            myDog.sleep();  // This method is still inherited from Animal
    
            System.out.println("\nDog details:");
            System.out.println("Name: " + myDog.getName());
            System.out.println("Age: " + myDog.getAge());
            System.out.println("Breed: " + myDog.getBreed());
        }
    }
  4. Guarda el archivo InheritanceDemo.java.

  5. Compila y ejecuta el programa actualizado:

    javac Animal.java Dog.java InheritanceDemo.java
    java InheritanceDemo
Method overriding output example

Deberías ver una salida similar a esta:

Demonstrating method overriding:
Generic Animal is eating.
Buddy is eating dog food.

Demonstrating inherited method:
Buddy is sleeping.

Dog details:
Name: Buddy
Age: 3
Breed: Labrador

Esto demuestra cómo la sobrescritura de métodos permite a la clase Dog proporcionar su propia implementación del método eat(), mientras sigue heredando otros métodos como sleep() de la clase Animal.

Introducción al Polimorfismo

El polimorfismo es un concepto fundamental en la programación orientada a objetos que nos permite utilizar una referencia de clase base para referirnos a un objeto de subclase. Esto permite un código más flexible y reutilizable. Veamos cómo funciona.

  1. Crea un nuevo archivo llamado Cat.java:

    touch Cat.java
  2. Abre Cat.java y agrega el siguiente código:

    public class Cat extends Animal {
        public Cat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println(getName() + " is eating fish.");
        }
    
        public void meow() {
            System.out.println(getName() + " says: Meow!");
        }
    }

    Esto crea otra subclase de Animal con su propio método eat() y un nuevo método meow().

  3. Guarda el archivo Cat.java.

  4. Ahora, actualicemos nuestro InheritanceDemo.java para demostrar el polimorfismo. Reemplaza su contenido con:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal[] animals = new Animal[3];
            animals[0] = new Animal("Generic Animal", 5);
            animals[1] = new Dog("Buddy", 3, "Labrador");
            animals[2] = new Cat("Whiskers", 2);
    
            System.out.println("Demonstrating polymorphism:");
            for (Animal animal : animals) {
                animal.eat();  // This will call the appropriate eat() method for each animal
            }
    
            System.out.println("\nAccessing specific methods:");
            ((Dog) animals[1]).getBreed();  // We need to cast to Dog to call Dog-specific methods
            ((Cat) animals[2]).meow();      // We need to cast to Cat to call Cat-specific methods
        }
    }

    Este código crea un array de objetos Animal, pero en realidad estamos almacenando una mezcla de objetos Animal, Dog y Cat en él. Cuando llamamos a eat() en cada animal, Java llama automáticamente a la versión adecuada del método según el tipo real del objeto.

  5. Guarda el archivo InheritanceDemo.java.

  6. Compila y ejecuta el programa actualizado:

    javac Animal.java Dog.java Cat.java InheritanceDemo.java
    java InheritanceDemo

    Deberías ver una salida similar a esta:

    Demonstrating polymorphism:
    Generic Animal is eating.
    Buddy is eating dog food.
    Whiskers is eating fish.
    
    Accessing specific methods:
    Whiskers says: Meow!

Esto demuestra el polimorfismo en acción. Somos capaces de tratar todos los objetos como objetos Animal, pero cuando llamamos al método eat(), cada objeto se comporta según la implementación específica de su clase.

Resumen

En este laboratorio, hemos explorado algunos conceptos clave de la programación orientada a objetos en Java:

  1. Herencia: Creamos una clase base Animal y derivamos las clases Dog y Cat de ella. Esto nos permitió reutilizar código y crear una jerarquía lógica de clases.
  2. Sobrescritura de Métodos: Vimos cómo las subclases pueden proporcionar sus propias implementaciones de métodos definidos en la superclase, lo que permite un comportamiento más específico.
  3. Polimorfismo: Aprendimos cómo tratar objetos de diferentes clases de manera uniforme a través de su superclase común, lo que permite un código más flexible y reutilizable.

Estos conceptos son fundamentales para Java y la programación orientada a objetos en general. Nos permiten crear estructuras de código más organizadas, eficientes y flexibles. A medida que continúes tu viaje en Java, encontrarás que estos conceptos se utilizan ampliamente en aplicaciones más complejas.

Recuerda, la práctica es la clave para dominar estos conceptos.