Encapsulación Herencia Polimorfismo

Encapsulación, Herencia, Polimorfismo

Estos tres conceptos son los pilares fundamentales de la programación orientada a objetos (OOP) en Java, y permiten construir aplicaciones más modulares, reutilizables y fáciles de mantener. A continuación, te explico cada uno de estos conceptos con ejemplos y detalles.


1. Encapsulación

La encapsulación es el principio de ocultar los detalles internos de una clase y exponer solo lo necesario para interactuar con ella. Esto significa que los atributos (variables) de una clase no se acceden directamente desde fuera de la clase, sino que se manipulan a través de métodos públicos conocidos como getters y setters.

¿Por qué es importante la encapsulación?

  • Protege el estado interno del objeto.
  • Permite controlar cómo se accede y modifica el estado del objeto.
  • Fomenta la reutilización del código y mejora la seguridad.

Componentes de la Encapsulación:

  1. Atributos privados: Los atributos de una clase son privados, lo que significa que no pueden ser accedidos directamente desde fuera de la clase.
  2. Métodos públicos (getters y setters): Se proporcionan métodos públicos para acceder y modificar estos atributos.

Ejemplo de Encapsulación:

public class Persona {
    // Atributos privados
    private String nombre;
    private int edad;

    // Getter para el nombre
    public String getNombre() {
        return nombre;
    }

    // Setter para el nombre
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    // Getter para la edad
    public int getEdad() {
        return edad;
    }

    // Setter para la edad
    public void setEdad(int edad) {
        if (edad > 0) {
            this.edad = edad;  // Se valida la edad antes de modificarla
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Persona persona = new Persona();

        // Usando setters para modificar atributos privados
        persona.setNombre("Juan");
        persona.setEdad(30);

        // Usando getters para acceder a los atributos privados
        System.out.println("Nombre: " + persona.getNombre());
        System.out.println("Edad: " + persona.getEdad());
    }
}

Salida esperada:

Nombre: Juan
Edad: 30

En este ejemplo, los atributos nombre y edad son privados y no se pueden acceder directamente desde fuera de la clase. En su lugar, usamos los métodos getNombre(), getEdad(), setNombre() y setEdad() para obtener y modificar sus valores.


2. Herencia

La herencia es el mecanismo que permite a una clase heredar propiedades y comportamientos de otra clase. En Java, la herencia se implementa mediante la palabra clave extends. La clase que hereda es conocida como subclase o clase hija, y la clase de la que hereda es la superclase o clase padre.

¿Por qué es importante la herencia?

  • Facilita la reutilización del código.
  • Permite organizar las clases de manera jerárquica.
  • Promueve la creación de clases más específicas que pueden extender el comportamiento de clases más generales.

Ejemplo de Herencia:

// Clase base o superclase
public class Animal {
    public void hacerSonido() {
        System.out.println("El animal hace un sonido.");
    }
}

// Clase derivada o subclase
public class Perro extends Animal {
    // Método sobreescrito
    @Override
    public void hacerSonido() {
        System.out.println("El perro ladra.");
    }
}

public class Main {
    public static void main(String[] args) {
        // Creación de un objeto de la subclase
        Animal miAnimal = new Animal();
        miAnimal.hacerSonido();  // Salida: El animal hace un sonido.

        Perro miPerro = new Perro();
        miPerro.hacerSonido();  // Salida: El perro ladra.
    }
}

Salida esperada:

El animal hace un sonido.
El perro ladra.

En este ejemplo:

  • Animal es la superclase que tiene un método hacerSonido().
  • Perro es la subclase que extiende de Animal y sobrescribe el método hacerSonido().
  • Cuando se llama a hacerSonido() en un objeto Perro, se ejecuta la versión sobrescrita en la subclase.

3. Polimorfismo

El polimorfismo es la capacidad de un objeto de tomar muchas formas. En otras palabras, permite que un objeto de una subclase sea tratado como un objeto de la superclase. Existen dos tipos de polimorfismo en Java:

  1. Polimorfismo de método (sobrecarga): Permite definir varios métodos con el mismo nombre pero con diferentes parámetros en la misma clase.
  2. Polimorfismo de inclusión (sobrescritura): Permite que un método de una subclase sobrescriba un método de su superclase.

¿Por qué es importante el polimorfismo?

  • Facilita la extensión del código sin modificar las clases existentes.
  • Mejora la flexibilidad del código, ya que puedes tratar objetos de diferentes clases de manera uniforme.

Ejemplo de Polimorfismo de Inclusión (Sobrescritura):

// Clase base o superclase
public class Animal {
    public void hacerSonido() {
        System.out.println("El animal hace un sonido.");
    }
}

// Clase derivada o subclase
public class Gato extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("El gato maúlla.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal miAnimal = new Animal();
        Animal miGato = new Gato();  // Polimorfismo: Animal referencia a un objeto Gato

        miAnimal.hacerSonido();  // Salida: El animal hace un sonido.
        miGato.hacerSonido();    // Salida: El gato maúlla.
    }
}

Salida esperada:

El animal hace un sonido.
El gato maúlla.

En este ejemplo, la referencia miGato es de tipo Animal, pero en realidad, hace referencia a un objeto de la clase Gato. Gracias al polimorfismo, cuando se llama a hacerSonido() en la referencia miGato, se ejecuta la versión sobrescrita del método hacerSonido() de la clase Gato, no la de Animal.

Polimorfismo de Método (Sobrecarga):

public class Calculadora {
    public int sumar(int a, int b) {
        return a + b;
    }

    public double sumar(double a, double b) {
        return a + b;
    }

    public int sumar(int a, int b, int c) {
        return a + b + c;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculadora calc = new Calculadora();

        System.out.println(calc.sumar(2, 3));         // Llama a sumar(int, int)
        System.out.println(calc.sumar(2.5, 3.5));     // Llama a sumar(double, double)
        System.out.println(calc.sumar(1, 2, 3));      // Llama a sumar(int, int, int)
    }
}

Salida esperada:

5
6.0
6

En este ejemplo, el método sumar() está sobrecargado para aceptar diferentes tipos y cantidades de parámetros, lo que demuestra el polimorfismo de método.