Interfaces en Java
1. Introducción
En este tutorial, vamos a hablar sobre las interfaces en Java. También veremos cómo Java las utiliza para implementar polimorfismo y la herencia múltiple.
2. ¿Qué Son las Interfaces en Java?
En Java, una interfaz es un tipo abstracto que contiene una colección de métodos y variables constantes. Es uno de los conceptos fundamentales en Java y se utiliza para lograr la abstracción, polimorfismo y la herencia múltiple.
Veamos un ejemplo simple de una interfaz en Java:
public interface Electronico {
// Variable constante
String LED = "LED";
// Método abstracto
int getConsumoDeElectricidad();
// Método estático
static boolean esEficienteDesdeElPuntoDeVistaEnergetico(String tipoElectronico) {
if (tipoElectronico.equals(LED)) {
return true;
}
return false;
}
// Método predeterminado
default void imprimirDescripcion() {
System.out.println("Descripción Electrónica");
}
}
Podemos implementar una interfaz en una clase Java utilizando la palabra clave implements.
A continuación, creemos también una clase Computadora
que implemente la interfaz Electronico
que acabamos de crear:
public class Computadora implements Electronico {
@Override
public int getConsumoDeElectricidad() {
return 1000;
}
}
2.1 Reglas para Crear Interfaces
En una interfaz, se nos permite usar:
También debemos recordar que:
- No podemos instanciar interfaces directamente.
- Una interfaz puede estar vacía, sin métodos ni variables.
- No podemos usar la palabra clave final en la definición de la interfaz, ya que dará como resultado un error de compilación.
- Todas las declaraciones de interfaz deben tener el modificador de acceso public o default; el modificador abstract se agregará automáticamente por el compilador.
- Un método de interfaz no puede ser protected o final.
- Hasta Java 9, los métodos de interfaz no podían ser privados; sin embargo, Java 9 introdujo la posibilidad de definir métodos privados en interfaces.
- Las variables de interfaz son public, static y final por definición; no se nos permite cambiar su visibilidad.
3. ¿Qué Podemos Lograr Usándolas?
3.1 Funcionalidad de Comportamiento
Utilizamos interfaces para agregar cierta funcionalidad de comportamiento que puede ser utilizada por clases no relacionadas. Por ejemplo, Comparable
, Comparator
y Cloneable
son interfaces de Java que pueden ser implementadas por clases no relacionadas. A continuación, se muestra un ejemplo de la interfaz Comparator
que se utiliza para comparar dos instancias de la clase Empleado
:
public class Empleado {
private double salario;
public double getSalario() {
return salario;
}
public void setSalario(double salario) {
this.salario = salario;
}
}
public class ComparadorDeSalarioEmpleado implements Comparator<Empleado> {
@Override
public int compare(Empleado empleadoA, Empleado empleadoB) {
if (empleadoA.getSalario() < empleadoB.getSalario()) {
return -1;
} else if (empleadoA.getSalario() > empleadoB.getSalario()) {
return 1;
} else {
return 0;
}
}
}
3.2 Herencia Múltiple
Las clases Java admiten la herencia singular. Sin embargo, mediante el uso de interfaces, también podemos implementar la herencia múltiple.
Por ejemplo, en el siguiente ejemplo, notamos que la clase Coche
implementa las interfaces Volar
y Transformar
. Al hacerlo, hereda los métodos volar
y transformar
:
public interface Transformar {
void transformar();
}
public interface Volar {
void volar();
}
public class Coche implements Volar, Transformar {
@Override
public void volar() {
System.out.println("¡Puedo Volar!");
}
@Override
public void transformar() {
System.out.println("¡Puedo Transformarme!");
}
}
3.3 Polimorfismo
Comencemos haciendo la pregunta: ¿qué es el polimorfismo? Es la capacidad de un objeto para tomar diferentes formas durante la ejecución. Para ser más específicos, es la ejecución del método sobrescrito que está relacionado con un tipo de objeto específico en tiempo de ejecución.
En Java, podemos lograr el polimorfismo utilizando interfaces. Por ejemplo, la interfaz Forma
puede tomar diferentes formas: puede ser un Circulo
o un Cuadrado
.
Comencemos definiendo la interfaz Forma
:
public interface Forma {
String nombre();
}
Ahora también creemos la clase Circulo
:
public class Círculo implements Forma {
@Override
public String nombre() {
return "Círculo";
}
}
Y también la clase Cuadrado
:
public class Cuadrado implements Forma {
@Override
public String nombre() {
return "Cuadrado";
}
}
Finalmente, es hora de ver el polimorfismo en acción utilizando nuestra interfaz Forma
y sus implementaciones. Creemos algunas instancias de objetos Forma
, agréguelas a una List
y, finalmente, imprima sus nombres en un bucle:
List<Forma> formas = new ArrayList<>();
Forma formaCirculo =
new Círculo();
Forma formaCuadrado = new Cuadrado();
formas.add(formaCirculo);
formas.add(formaCuadrado);
for (Forma forma : formas) {
System.out.println(forma.nombre());
}
4. Métodos default en Interfaces
Las interfaces tradicionales en Java 7 y anteriores no ofrecen compatibilidad inversa.
Lo que esto significa es que si tienes código heredado escrito en Java 7 o anterior, y decides agregar un método abstract a una interfaz existente, entonces todas las clases que implementen esa interfaz deben sobrescribir el nuevo método abstract. De lo contrario, el código se romperá.
Java 8 resolvió este problema al introducir el método default, que es opcional y puede implementarse a nivel de la interfaz.
5. Reglas de Herencia de Interfaces
Para lograr la herencia múltiple a través de interfaces, debemos recordar algunas reglas. Veamos estas en detalle.
5.1 Interfaz que Extiende Otra Interfaz
Cuando una interfaz extiende (extends) otra interfaz, hereda todos los métodos abstractos de esa interfaz. Comencemos creando dos interfaces, TieneColor
y Forma
:
public interface TieneColor {
String obtenerColor();
}
public interface Caja extends TieneColor {
int obtenerAltura();
}
En el ejemplo anterior, Caja
hereda de TieneColor
utilizando la palabra clave extends
. Al hacerlo, la interfaz Caja
hereda obtenerColor
. Como resultado, la interfaz Caja
ahora tiene dos métodos: obtenerColor
y obtenerAltura
.
5.2 Clase Abstracta que Implementa una Interfaz
Cuando una clase abstracta implementa una interfaz, hereda todos sus métodos abstract y default. Consideremos la interfaz Transformar
y la clase abstracta Vehículo
que la implementa:
public interface Transformar {
void transformar();
default void imprimirEspecificaciones(){
System.out.println("Especificaciones de Transformación");
}
}
public abstract class Vehículo implements Transformar {}
En este ejemplo, la clase Vehículo
hereda dos métodos: el método abstracto transformar
y el método default imprimirEspecificaciones
.
6. Interfaces Funcionales
Java ha tenido muchas interfaces funcionales desde sus primeros días, como Comparable
(desde Java 1.2) y Runnable
(desde Java 1.0).
Java 8 introdujo nuevas interfaces funcionales como Predicate
, Consumer
y Function
. Para obtener más información sobre estas, visita nuestro tutorial sobre Interfaces Funcionales en Java 8 en futuras lecciones.
7. Conclusión
En este tutorial, ofrecimos una visión general de las interfaces en Java y hablamos sobre cómo usarlas para lograr polimorfismo y la herencia múltiple.