Saltar a contenido

Semana 8 - Clases Abstractas en Java

Las clases abstractas en Java son una herramienta poderosa para la programación orientada a objetos, que permite definir la estructura y el comportamiento general de una clase sin tener que implementar todos sus métodos.

¿Qué son las clases abstractas?

  • Plantillas incompletas: Las clases abstractas actúan como plantillas incompletas para otras clases. No se pueden instanciar directamente (crear objetos de ellas), pero sirven como base para clases concretas que las heredan y completan su implementación.
  • Métodos abstractos: Una clase abstracta puede contener métodos abstractos, que se declaran pero no se implementan. La responsabilidad de implementar estos métodos recae en las clases que heredan de la clase abstracta.
  • Definición de comportamiento general: Las clases abstractas definen un comportamiento general para un tipo de objeto, dejando la implementación específica a las clases derivadas.
  • Abstracción: El concepto de clase abstracta promueve la abstracción, ocultando la complejidad de la implementación y mostrando únicamente la interfaz necesaria.

Ejemplo:

Imaginemos que estamos creando un sistema de gestión de vehículos. Podemos definir una clase abstracta llamada "Vehiculo":

public abstract class Vehiculo {
  private String marca;
  private String modelo;

  public Vehiculo(String marca, String modelo) {
    this.marca = marca;
    this.modelo = modelo;
  }

  public String getMarca() {
    return marca;
  }

  public String getModelo() {
    return modelo;
  }

  // Método abstracto que define el comportamiento de moverse
  public abstract void moverse();
}

En este ejemplo:

  • Vehiculo es una clase abstracta, definida con la palabra clave abstract.
  • Tiene atributos privados para marca y modelo, y un constructor para inicializarlos.
  • También tiene métodos para obtener la marca y el modelo del vehículo.
  • El método moverse() es abstracto, ya que solo se declara (con abstract) pero no se implementa.

¿Cómo se utilizan las clases abstractas?

  1. Herencia: Las clases concretas heredan de la clase abstracta.
  2. Implementación de métodos abstractos: Las clases derivadas deben implementar todos los métodos abstractos de la clase padre.
  3. Especialización del comportamiento: Cada clase derivada puede especializar el comportamiento definido en la clase abstracta.

Ejemplo de herencia:

public class Coche extends Vehiculo {

  public Coche(String marca, String modelo) {
    super(marca, modelo);
  }

  @Override
  public void moverse() {
    System.out.println("El coche se mueve sobre ruedas");
  }
}

public class Avion extends Vehiculo {

  public Avion(String marca, String modelo) {
    super(marca, modelo);
  }

  @Override
  public void moverse() {
    System.out.println("El avión se mueve en el aire");
  }
}

En este ejemplo:

  • Coche y Avion son clases concretas que heredan de la clase abstracta Vehiculo.
  • Ambas clases implementan el método abstracto moverse() con su propia lógica específica.

Ventajas de las clases abstractas:

  • Reutilización de código: Se reduce la duplicación de código al definir el comportamiento general en una sola clase abstracta.
  • Abstracción: Se facilita la abstracción de conceptos, ocultando detalles de implementación.
  • Polimorfismo: Permite crear diferentes tipos de objetos que comparten una misma interfaz (el método moverse() en este caso).

Métodos Abstractos en Java: La Base de la Abstracción

Los métodos abstractos son la pieza clave de las clases abstractas en Java. Son como promesas de comportamiento que se declaran, pero no se implementan. La responsabilidad de definir la lógica de estos métodos recae en las clases que heredan de la clase abstracta.

Características de los métodos abstractos:

  • Solo declaración: Los métodos abstractos solo se declaran, usando la palabra clave abstract antes de la declaración del método.
  • No tienen cuerpo: Los métodos abstractos no tienen cuerpo de código entre llaves ({}).
  • Deben ser implementados: Las clases derivadas (hijas) que heredan de la clase abstracta deben implementar todos los métodos abstractos.
  • Implementación específica: La implementación del método abstracto puede variar entre las clases derivadas, lo que permite especializar el comportamiento.

Ejemplo:

public abstract class Animal {

  // Método abstracto para definir el sonido que emite un animal
  public abstract void hacerSonido();

  // Otros métodos pueden ser concretos
  public void comer() {
    System.out.println("El animal está comiendo");
  }
}

En este ejemplo:

  • hacerSonido() es un método abstracto, solo declarado pero no implementado.
  • Las clases que hereden de Animal (como Perro o Gato) deberán implementar hacerSonido() con su propio comportamiento específico.
  • El método comer() es concreto, ya que tiene un cuerpo definido.

Ventajas de los métodos abstractos:

  • Abstracción: Permiten definir un comportamiento común sin necesidad de conocer la implementación exacta.
  • Flexibilidad: La implementación del método abstracto puede variar entre las clases derivadas, lo que permite adaptarse a diferentes necesidades.
  • Polimorfismo: Facilitan el polimorfismo, ya que las clases derivadas pueden responder de forma diferente al mismo método.
  • Reutilización: Promueven la reutilización de código, al definir el comportamiento general en la clase abstracta.

Ejemplos de uso:

  • Interfaces gráficas: Se pueden definir métodos abstractos para el comportamiento de botones, menus, etc. Cada tipo de botón o menu implementaría estos métodos de forma específica.
  • Sistemas de gestión: Se pueden definir métodos abstractos para operaciones comunes en diferentes tipos de entidades (usuarios, productos, etc.).
  • Diseño de patrones: Se pueden utilizar para implementar patrones como "Factory Method" o "Template Method".

Ejemplo de Clase Abstracta con Métodos Abstractos: Sistema de Transporte Público

Este ejemplo ilustra cómo usar clases abstractas y métodos abstractos para modelar un sistema de transporte público en Colombia, con énfasis en la diversidad de modalidades existentes.

Clase Abstracta:

public abstract class TransportePublico {
  private String nombreCompania;
  private String tipoTransporte;

  public TransportePublico(String nombreCompania, String tipoTransporte) {
    this.nombreCompania = nombreCompania;
    this.tipoTransporte = tipoTransporte;
  }

  public String getNombreCompania() {
    return nombreCompania;
  }

  public String getTipoTransporte() {
    return tipoTransporte;
  }

  // Métodos abstractos:
  public abstract double calcularTarifa(double distancia);
  public abstract String getMedioPago();
  public abstract String getRuta();
  public abstract int getCapacidad();

  // Método concreto:
  public void mostrarInformacion() {
    System.out.println("Nombre de la compañía: " + nombreCompania);
    System.out.println("Tipo de transporte: " + tipoTransporte);
  }
}

Explicación:

  • La clase TransportePublico es abstracta, lo que indica que no se pueden crear instancias directas de ella.
  • Tiene atributos para el nombre de la compañía y el tipo de transporte (ej. "Bus", "Metro", "TransMilenio").
  • Contiene métodos abstractos:
    • calcularTarifa(double distancia): calcula la tarifa de acuerdo a la distancia, pero la implementación es específica para cada tipo de transporte.
    • getMedioPago(): devuelve los medios de pago aceptados (ej. "Efectivo", "Tarjeta", "App").
    • getRuta(): devuelve la ruta que recorre el transporte (ej. "Estación A - Estación B").
    • getCapacidad(): devuelve la capacidad máxima de pasajeros.
  • Tiene un método concreto mostrarInformacion() que muestra información general sobre el transporte.

Clases Derivadas:

// Clase para buses
public class Bus extends TransportePublico {

  public Bus(String nombreCompania) {
    super(nombreCompania, "Bus");
  }

  @Override
  public double calcularTarifa(double distancia) {
    // Lógica específica para calcular la tarifa de un bus
    return 2000 + distancia * 100; // Ejemplo simple
  }

  @Override
  public String getMedioPago() {
    return "Efectivo, Tarjeta";
  }

  @Override
  public String getRuta() {
    // Implementación específica para la ruta del bus
    return "Terminal - Centro"; 
  }

  @Override
  public int getCapacidad() {
    return 50; // Capacidad típica de un bus
  }
}

// Clase para el Metro de Medellín
public class Metro extends TransportePublico {

  public Metro(String nombreCompania) {
    super(nombreCompania, "Metro");
  }

  @Override
  public double calcularTarifa(double distancia) {
    // Lógica específica para calcular la tarifa del metro
    return 2500; // Tarifa fija en el Metro de Medellín
  }

  @Override
  public String getMedioPago() {
    return "Tarjeta Cívica";
  }

  @Override
  public String getRuta() {
    // Implementación específica para la ruta del metro
    return "Estación A - Estación Z";
  }

  @Override
  public int getCapacidad() {
    return 100; // Capacidad típica de un tren de metro
  }
}

Explicación:

  • Se crean clases derivadas específicas para "Bus" y "Metro".
  • Cada clase implementa los métodos abstractos de TransportePublico con la lógica específica para ese tipo de transporte.

Uso del Ejemplo:

public class EjemploTransporte {

  public static void main(String[] args) {
    // Crear un objeto Bus
    Bus bus = new Bus("Transmilenio");
    bus.mostrarInformacion();
    System.out.println("Tarifa: " + bus.calcularTarifa(10));

    // Crear un objeto Metro
    Metro metro = new Metro("Metro de Medellín");
    metro.mostrarInformacion();
    System.out.println("Tarifa: " + metro.calcularTarifa(5));
  }
}

Beneficios de esta implementación:

  • Abstracción: La clase TransportePublico define el comportamiento general de un sistema de transporte público, sin preocuparse por detalles específicos de cada tipo de transporte.
  • Flexibilidad: Se pueden añadir nuevos tipos de transporte (ej. "Tranvía", "Cable Aéreo") simplemente creando nuevas clases derivadas de TransportePublico.
  • Reutilización de código: El código común se define en la clase abstracta, evitando la duplicación en las clases derivadas.

Actividad: Simulando un Sistema de Gestión de Mascotas

Objetivo: Implementar un sistema de gestión de mascotas en Java utilizando clases abstractas y métodos abstractos.

Descripción:

El sistema de gestión de mascotas debe incluir:

  • Clase Abstracta Mascota:
    • Atributos: nombre, especie, raza, edad, tamaño, color, estado de salud.
    • Métodos abstractos:
      • hacerSonido(): Emita el sonido característico de la mascota.
      • alimentar(): Describe cómo se alimenta la mascota.
      • cuidar(): Describe cómo se cuida la mascota (bañarse, cepillarse, etc.).
    • Método concreto:
      • mostrarInformacion(): Muestra información general de la mascota.
  • Clases Concretas:
    • Perro
    • Gato
    • Pajaro
    • Tortuga

Pistas y Guías:

Paso 1: Definición de la Clase Abstracta Mascota

  • Crea un paquete: com.mascotas.gestion
  • Define la clase Mascota:
    • Declara los atributos (nombre, especie, raza, edad, tamaño, color, estado de salud).
    • Crea el constructor para inicializar los atributos.
    • Define los métodos getNombre(), getEspecie(), getRaza(), getEdad(), getTamaño(), getColor(), getEstadoSalud().
    • Declara los métodos abstractos hacerSonido(), alimentar(), cuidar().
    • Define el método concreto mostrarInformacion().

Paso 2: Creación de Clases Concretas

  • Crea las clases: Perro, Gato, Pajaro, Tortuga.
  • Extiende cada clase de Mascota: public class Perro extends Mascota { ... }
  • Implementa los métodos abstractos en cada clase:
    • hacerSonido(): Define el sonido que emite la mascota (ladrar, maullar, cantar, etc.).
    • alimentar(): Describe la forma de alimentar a la mascota (croquetas, alimento balanceado, frutas, etc.).
    • cuidar(): Describe cómo se cuida a la mascota (paseos, baño, cepillado, etc.).

Paso 3: Clase Principal Main.java

  • Crea la clase Main en el paquete com.mascotas.gestion:
  • Crea objetos de cada tipo de mascota:
    • Ejemplo: Perro perro = new Perro("Max", "Canino", "Labrador", 3, "Mediano", "Dorado", "Saludable");
  • Prueba las funcionalidades:
    • Llama a los métodos hacerSonido(), alimentar(), cuidar(), mostrarInformacion().
  • Imprime los resultados en la consola.

Ejemplo de código:

// com.mascotas.gestion.Mascota.java
package com.mascotas.gestion;

public abstract class Mascota {
    // ... (Atributos, Constructor, Métodos)
}

// com.mascotas.gestion.Perro.java
package com.mascotas.gestion;

public class Perro extends Mascota {
    // ... (Constructor, Implementación de Métodos Abstractos)
}

// com.mascotas.gestion.Main.java
package com.mascotas.gestion;

public class Main {
    public static void main(String[] args) {
        // ... (Creación de objetos y pruebas)
    }
}

Entrega:

  1. Sube tu código al repositorio de GitHub.
  2. Comparte el enlace de tu repositorio de GitHub en el archivo de evidencias de tu carpeta compartida en la semana correspondiente.