Una interfaz es un tipo que define un contrato de operaciones sin fijar una implementación concreta.
En Java, una clase solo puede extender otra clase, pero puede implementar varias interfaces.
Imagina que tenemos la clase Coche.
- Un Coche es un
Vehiculo. - Pero un Coche también puede ser un
ActivoFinanciero(se puede comprar/vender). - Y también puede ser
Localizable(tiene coordenadas GPS).
¿Cómo hacemos que Coche sea las tres cosas a la vez si solo podemos extender de una?
La respuesta son las Interfaces.
¿Qué es una interfaz?
Una Interfaz es un contrato. No define cómo se hacen las cosas, solo define QUÉ se debe hacer. Es una lista de requisitos que una clase debe cumplir para ser considerada “compatible” con esa interfaz.
Sintaxis: interface e implements
Se definen con la palabra clave interface.
Las declaraciones de métodos sin cuerpo son implícitamente public y abstract. Las interfaces modernas también pueden incluir métodos default, static y private con implementación.
// Definimos el contrato "Se puede localizar por GPS"
public interface Localizable {
// No hace falta poner 'public abstract', ya lo es.
void obtenerCoordenadas();
}
// Otro contrato independiente
public interface ActivoFinanciero {
double getValorMercado();
}
Ahora, hacemos que nuestro Coche firme esos contratos usando implements.
// Heredamos de Vehiculo e implementamos varias interfaces
public class Coche extends Vehiculo implements Localizable, ActivoFinanciero {
// ... constructor y métodos de Coche ...
// OBLIGATORIO: Cumplir el contrato de Localizable
@Override
public void obtenerCoordenadas() {
System.out.println("GPS: 40.416, -3.703");
}
// OBLIGATORIO: Cumplir el contrato de ActivoFinanciero
@Override
public double getValorMercado() {
return 25000.00;
}
}
Gracias a esto, hemos logrado la Herencia Múltiple de Tipos. Nuestro objeto ahora es polimórfico en 4 direcciones: es un Object, es un Vehiculo, es un Localizable y es un ActivoFinanciero.
Coche miCoche = new Coche();
Localizable gps = miCoche; // Válido
gps.obtenerCoordenadas();
ActivoFinanciero cartera = miCoche; // Válido
System.out.println(cartera.getValorMercado());
Interfaces vs clases abstractas
Es la duda eterna. ¿Cuándo uso una y cuándo otra?
| Característica | Clase Abstracta | Interfaz |
|---|---|---|
| Relación | “Es un” (Identidad). | “Es capaz de” (Capacidad / Comportamiento). |
| Estado | Puede tener atributos (campos) con estado. | Solo puede tener constantes (public static final). |
| Herencia | Solo una (extends). | Múltiples (implements). |
| Constructores | Sí tiene. | No tiene. |
- Usa Clase Abstracta si quieres compartir código y estado entre clases muy relacionadas (Perro y Gato comparten el atributo
nombre). - Usa Interfaz si quieres definir un comportamiento común para clases que no tienen nada que ver (un
Cochey unMovilsonLocalizable, pero no son familia).
Métodos default
Un método default es un método en una interfaz que SÍ tiene cuerpo (código).
public interface Localizable {
void obtenerCoordenadas(); // Abstracto (Obligatorio)
// Método DEFAULT (Opcional)
// Si la clase no lo sobrescribe, se usa este código por defecto.
default void imprimirUbicacion() {
System.out.println("Imprimiendo coordenadas en consola...");
obtenerCoordenadas();
}
}
Ahora, si actualizas la interfaz añadiendo un método default, las clases antiguas siguen funcionando porque usan la implementación por defecto.
El problema de la herencia múltiple (resuelto)
Si implementas dos interfaces y ambas tienen un método default idéntico (mismo nombre), ¿cuál gana?
Java te dará un error de compilación y te obligará a sobrescribirlo en tu clase para decidir manualmente a cuál llamar (usando Interfaz.super.metodo()).
Métodos static y private
Con las versiones modernas, las interfaces se han vuelto muy potentes:
- Métodos
static(Java 8): Métodos de utilidad asociados a la interfaz (ej:Localizable.calcularDistancia(p1, p2)). - Métodos
private(Java 9): Sirven para no repetir código dentro de la propia interfaz, compartiendo lógica entre dos métodosdefault.