Semana 11 - Interfaces en Java
📚 ¿Qué es una Interfaz?
Una interfaz en Java es un contrato que define qué métodos debe implementar una clase, pero no especifica cómo implementarlos. Es como un "acuerdo" o "promesa" que una clase hace sobre qué comportamientos va a proporcionar.
🔍 Características Principales
- Contrato de comportamiento: Define QUÉ debe hacer una clase, no CÓMO
- Abstracción pura: Todos los métodos son abstractos por defecto (hasta Java 8)
- Implementación múltiple: Una clase puede implementar varias interfaces
- Sin estado: No pueden tener variables de instancia (solo constantes)
- Herencia múltiple de comportamiento: Permite simular herencia múltiple
🌟 Analogía del Mundo Real
Imagina una licencia de conducir: - La licencia es como una interfaz que dice "esta persona puede conducir" - No importa si conduces un carro, moto o camión (diferentes implementaciones) - Todos los conductores deben saber acelerar, frenar y girar (métodos obligatorios) - Cada vehículo implementa estos comportamientos de manera diferente
// La "licencia" sería nuestra interfaz
interface Conductor {
void acelerar();
void frenar();
void girar(String direccion);
}
🛠️ Declaración de una Interfaz
Sintaxis Básica
[modificador] interface NombreInterfaz {
// Constantes (implícitamente public, static, final)
tipo CONSTANTE = valor;
// Métodos abstractos (implícitamente public y abstract)
tipoRetorno nombreMetodo(parametros);
}
📝 Ejemplo Básico
public interface Vehiculo {
// Constante - velocidad máxima permitida
int VELOCIDAD_MAXIMA = 120;
// Métodos abstractos que deben implementar las clases
void encender();
void apagar();
void acelerar(int velocidad);
void frenar();
String obtenerTipo();
}
🎨 Ejemplo con Múltiples Métodos
public interface Reproductor {
// Constantes
int VOLUMEN_MAXIMO = 100;
int VOLUMEN_MINIMO = 0;
// Métodos que debe implementar cualquier reproductor
void reproducir();
void pausar();
void detener();
void siguienteCancion();
void cancionAnterior();
void ajustarVolumen(int volumen);
boolean estaReproduciendo();
}
⚡ Características de los Métodos en Interfaces
public interface EjemploCaracteristicas {
// ❌ INCORRECTO - no puede tener variables de instancia
// private String nombre;
// ✅ CORRECTO - constantes (public static final implícito)
String TIPO_DEFECTO = "Genérico";
int NUMERO_MAXIMO = 1000;
// ✅ CORRECTO - métodos abstractos (public abstract implícito)
void metodoAbstracto();
String obtenerInformacion();
// ❌ INCORRECTO - no puede tener constructores
// public EjemploCaracteristicas() { }
}
🔧 Implementación de una Interfaz - Uso de implements
Sintaxis de Implementación
public class NombreClase implements NombreInterfaz {
// Debe implementar TODOS los métodos de la interfaz
@Override
public tipoRetorno nombreMetodo(parametros) {
// Implementación específica
}
}
📱 Ejemplo Práctico: Sistema de Dispositivos
// 1. Declaramos la interfaz
public interface DispositivoElectronico {
String MARCA_DEFECTO = "Genérica";
void encender();
void apagar();
void mostrarEstado();
int obtenerConsumoEnergia();
}
// 2. Implementamos la interfaz en diferentes clases
public class Telefono implements DispositivoElectronico {
private boolean encendido;
private String modelo;
private int bateria;
public Telefono(String modelo, int bateria) {
this.modelo = modelo;
this.bateria = bateria;
this.encendido = false;
}
@Override
public void encender() {
if (bateria > 0) {
encendido = true;
System.out.println("📱 Teléfono " + modelo + " encendido");
} else {
System.out.println("❌ Batería agotada, no se puede encender");
}
}
@Override
public void apagar() {
encendido = false;
System.out.println("📱 Teléfono " + modelo + " apagado");
}
@Override
public void mostrarEstado() {
String estado = encendido ? "Encendido" : "Apagado";
System.out.println("📱 " + modelo + " - Estado: " + estado + " - Batería: " + bateria + "%");
}
@Override
public int obtenerConsumoEnergia() {
return encendido ? 15 : 2; // Watts
}
// Método específico del teléfono
public void llamar(String numero) {
if (encendido) {
System.out.println("📞 Llamando a " + numero);
} else {
System.out.println("❌ Enciende el teléfono primero");
}
}
}
public class Laptop implements DispositivoElectronico {
private boolean encendida;
private String marca;
private String procesador;
public Laptop(String marca, String procesador) {
this.marca = marca;
this.procesador = procesador;
this.encendida = false;
}
@Override
public void encender() {
encendida = true;
System.out.println("💻 Laptop " + marca + " iniciando sistema...");
System.out.println("💻 Sistema operativo cargado");
}
@Override
public void apagar() {
encendida = false;
System.out.println("💻 Cerrando aplicaciones...");
System.out.println("💻 Laptop " + marca + " apagada");
}
@Override
public void mostrarEstado() {
String estado = encendida ? "Encendida" : "Apagada";
System.out.println("💻 " + marca + " (" + procesador + ") - Estado: " + estado);
}
@Override
public int obtenerConsumoEnergia() {
return encendida ? 65 : 5; // Watts
}
// Método específico de la laptop
public void ejecutarPrograma(String programa) {
if (encendida) {
System.out.println("💻 Ejecutando " + programa);
} else {
System.out.println("❌ Enciende la laptop primero");
}
}
}
🎮 Ejemplo de Uso
public class PruebaDispositivos {
public static void main(String[] args) {
// Crear dispositivos
Telefono miTelefono = new Telefono("iPhone 15", 85);
Laptop miLaptop = new Laptop("Dell", "Intel i7");
// Usar como objetos específicos
System.out.println("=== USO ESPECÍFICO ===");
miTelefono.encender();
miTelefono.llamar("123-456-7890");
miLaptop.encender();
miLaptop.ejecutarPrograma("Visual Studio Code");
// Usar polimorfismo con la interfaz
System.out.println("\n=== POLIMORFISMO ===");
DispositivoElectronico[] dispositivos = {miTelefono, miLaptop};
for (DispositivoElectronico dispositivo : dispositivos) {
dispositivo.mostrarEstado();
System.out.println("Consumo: " + dispositivo.obtenerConsumoEnergia() + "W");
System.out.println("Marca por defecto: " + DispositivoElectronico.MARCA_DEFECTO);
System.out.println("---");
}
// Apagar todos los dispositivos
System.out.println("=== APAGANDO DISPOSITIVOS ===");
for (DispositivoElectronico dispositivo : dispositivos) {
dispositivo.apagar();
}
}
}
🔄 Implementación Múltiple
Una de las grandes ventajas de las interfaces es que una clase puede implementar múltiples interfaces.
📚 Ejemplo: Sistema de Biblioteca
// Interfaces separadas para diferentes comportamientos
public interface Prestable {
void prestar(String usuario);
void devolver();
boolean estaPrestado();
}
public interface Catalogable {
String obtenerCodigo();
String obtenerCategoria();
void actualizarUbicacion(String nuevaUbicacion);
}
public interface Renovable {
void renovar();
boolean puedeRenovarse();
int diasRestantes();
}
// Clase que implementa múltiples interfaces
public class Libro implements Prestable, Catalogable, Renovable {
private String titulo;
private String autor;
private String codigo;
private String categoria;
private String ubicacion;
private boolean prestado;
private String usuarioPrestamo;
private int diasPrestamo;
public Libro(String titulo, String autor, String codigo, String categoria) {
this.titulo = titulo;
this.autor = autor;
this.codigo = codigo;
this.categoria = categoria;
this.ubicacion = "Estantería Principal";
this.prestado = false;
this.diasPrestamo = 0;
}
// Implementación de Prestable
@Override
public void prestar(String usuario) {
if (!prestado) {
prestado = true;
usuarioPrestamo = usuario;
diasPrestamo = 14; // 2 semanas
System.out.println("📖 '" + titulo + "' prestado a " + usuario);
} else {
System.out.println("❌ El libro ya está prestado");
}
}
@Override
public void devolver() {
if (prestado) {
prestado = false;
System.out.println("📖 '" + titulo + "' devuelto por " + usuarioPrestamo);
usuarioPrestamo = null;
diasPrestamo = 0;
} else {
System.out.println("❌ El libro no está prestado");
}
}
@Override
public boolean estaPrestado() {
return prestado;
}
// Implementación de Catalogable
@Override
public String obtenerCodigo() {
return codigo;
}
@Override
public String obtenerCategoria() {
return categoria;
}
@Override
public void actualizarUbicacion(String nuevaUbicacion) {
this.ubicacion = nuevaUbicacion;
System.out.println("📍 Ubicación actualizada: " + nuevaUbicacion);
}
// Implementación de Renovable
@Override
public void renovar() {
if (prestado && puedeRenovarse()) {
diasPrestamo += 7; // Una semana más
System.out.println("🔄 Préstamo renovado. Nuevos días: " + diasPrestamo);
} else {
System.out.println("❌ No se puede renovar");
}
}
@Override
public boolean puedeRenovarse() {
return prestado && diasPrestamo <= 7; // Solo si quedan pocos días
}
@Override
public int diasRestantes() {
return prestado ? diasPrestamo : 0;
}
// Método específico del libro
public void mostrarInformacion() {
System.out.println("📚 " + titulo + " por " + autor);
System.out.println(" Código: " + codigo + " | Categoría: " + categoria);
System.out.println(" Ubicación: " + ubicacion);
if (prestado) {
System.out.println(" Prestado a: " + usuarioPrestamo + " | Días restantes: " + diasPrestamo);
} else {
System.out.println(" Estado: Disponible");
}
}
}
🧪 Prueba del Sistema
public class PruebaBiblioteca {
public static void main(String[] args) {
Libro libro = new Libro("Cien Años de Soledad", "Gabriel García Márquez", "LIT001", "Literatura");
// Mostrar información inicial
libro.mostrarInformacion();
// Probar funcionalidades de Prestable
System.out.println("\n=== PRÉSTAMO ===");
libro.prestar("Juan Pérez");
libro.mostrarInformacion();
// Probar funcionalidades de Catalogable
System.out.println("\n=== CATALOGACIÓN ===");
System.out.println("Código: " + libro.obtenerCodigo());
System.out.println("Categoría: " + libro.obtenerCategoria());
libro.actualizarUbicacion("Sección Préstamos");
// Probar funcionalidades de Renovable
System.out.println("\n=== RENOVACIÓN ===");
System.out.println("¿Puede renovarse? " + libro.puedeRenovarse());
System.out.println("Días restantes: " + libro.diasRestantes());
libro.renovar();
// Devolver el libro
System.out.println("\n=== DEVOLUCIÓN ===");
libro.devolver();
libro.mostrarInformacion();
}
}
🎯 Ventajas de las Interfaces
1. Flexibilidad en el Diseño
// Diferentes implementaciones de la misma interfaz
interface Notificacion {
void enviar(String mensaje);
}
class NotificacionEmail implements Notificacion {
public void enviar(String mensaje) {
System.out.println("📧 Email: " + mensaje);
}
}
class NotificacionSMS implements Notificacion {
public void enviar(String mensaje) {
System.out.println("📱 SMS: " + mensaje);
}
}
class NotificacionPush implements Notificacion {
public void enviar(String mensaje) {
System.out.println("🔔 Push: " + mensaje);
}
}
2. Polimorfismo Poderoso
public class ServicioNotificaciones {
public void enviarNotificacion(Notificacion notificacion, String mensaje) {
notificacion.enviar(mensaje); // No importa qué tipo específico sea
}
public static void main(String[] args) {
ServicioNotificaciones servicio = new ServicioNotificaciones();
// Mismo método, diferentes comportamientos
servicio.enviarNotificacion(new NotificacionEmail(), "Bienvenido!");
servicio.enviarNotificacion(new NotificacionSMS(), "Código: 1234");
servicio.enviarNotificacion(new NotificacionPush(), "Nueva actualización");
}
}
3. Desacoplamiento de Código
// ❌ MALO - Acoplado a implementación específica
class ProcesadorPagos {
private PayPal paypal = new PayPal();
public void procesar(double monto) {
paypal.pagar(monto); // Solo funciona con PayPal
}
}
// ✅ BUENO - Desacoplado usando interfaz
interface ProcesadorPago {
void procesar(double monto);
}
class ProcesadorPagosFlexible {
private ProcesadorPago procesador;
public ProcesadorPagosFlexible(ProcesadorPago procesador) {
this.procesador = procesador;
}
public void procesar(double monto) {
procesador.procesar(monto); // Funciona con cualquier implementación
}
}
🏋️ Ejercicios Prácticos
Ejercicio 1: Sistema de Formas Geométricas
// Crea una interfaz y implementaciones
interface Forma {
double calcularArea();
double calcularPerimetro();
void dibujar();
}
// Implementa para: Círculo, Rectángulo, Triángulo
Ejercicio 2: Sistema de Empleados
// Crea interfaces para diferentes roles
interface Trabajador {
void trabajar();
double calcularSalario();
}
interface Supervisor {
void supervisar();
void asignarTarea(String tarea);
}
// Implementa: Desarrollador, Gerente (que es Trabajador y Supervisor)
Ejercicio 3: Sistema de Vehículos
interface Vehiculo {
void acelerar();
void frenar();
int obtenerVelocidadMaxima();
}
interface Volador {
void despegar();
void aterrizar();
int obtenerAltitudMaxima();
}
// Implementa: Carro, Avión (que es Vehiculo y Volador)
✅ Mejores Prácticas
1. Nombres Descriptivos
// ✅ BUENO
interface Reproducible {
void reproducir();
}
// ❌ MALO
interface I {
void r();
}
2. Interfaces Cohesivas
// ✅ BUENO - Una responsabilidad clara
interface Calculadora {
double sumar(double a, double b);
double restar(double a, double b);
}
// ❌ MALO - Múltiples responsabilidades
interface CalculadoraYBaseDatos {
double sumar(double a, double b);
void guardarEnBaseDatos(String datos);
}
3. Usar Constantes Apropiadamente
interface ConfiguracionJuego {
int VIDAS_INICIALES = 3;
int PUNTOS_POR_NIVEL = 100;
String VERSION = "1.0";
void iniciarJuego();
void terminarJuego();
}