fbpx

Métodos default y static en JAVA 8

Antes de Java 8, las interfaces solo podían tener métodos abstractos públicos. No era posible agregar una nueva funcionalidad a la interfaz existente sin obligar a todas las clases de implementación a crear una implementación de los nuevos métodos, ni era posible crear métodos de interfaz con una implementación.

A partir de Java 8, las interfaces pueden tener métodos static y default que, a pesar de estar declarados en una interfaz, tienen un comportamiento definido.

Vídeo explicativo

Métodos default

Los métodos default se declaran utilizando la nueva palabra clave predeterminada. Estos son accesibles a través de la instancia de la clase de implementación y se pueden anular.

Agreguemos un método default a nuestra interfaz Vehiculo, que también hará una llamada al método static de esta interfaz:

public interface Vehiculo {
  void String getFabricante();

  default String getDescripcion() {
      return "El ha sido creado por" + getFabricante();
  }
}

Suponga que esta interfaz está implementada por la clase VehiculoImpl.

Para ejecutar el método default, se debe crear una instancia de esta clase:

Vehiculo vehiculo = new VehiculoImpl();
String overview = vehicle.getDescripcion();

default en herencia de múltiples interfaces

Los métodos de interfaz default son una característica bastante buena, pero hay algunas advertencias que vale la pena mencionar. Dado que Java permite que las clases implementen múltiples interfaces, es importante saber qué sucede cuando una clase implementa varias interfaces que definen los mismos métodos default.

Para comprender mejor este escenario, definamos una nueva interfaz de Alarma y refactoricemos la clase Coche:

public interface Alarma {

    default String encenderAlarma() {
        return "Alarma encendida...";
    }
    
    default String apagarAlarma() {
        return "Alarma apagada...";
    }
}

Con esta nueva interfaz que define su propio conjunto de métodos default, la clase Coche implementaría tanto Vehiculo como Alarma:

public class Ccoche implements Vehiculo, Alarma {
    // ...
}

En este caso, el código simplemente no se compilará, ya que hay un conflicto causado por la herencia de múltiples interfaces (también conocido como el problema del diamante). La clase Coche heredaría ambos conjuntos de métodos default. Entonces, ¿a cuáles deberíamos llamar?

Para resolver esta ambigüedad, debemos proporcionar explícitamente una implementación para los métodos:

@Override
public String encenderAlarma() {
    // ...
}
    
@Override
public String apagarAlarma() {
    // ...
}

También podemos hacer que nuestra clase use los métodos default de una de las interfaces.

Veamos un ejemplo que utiliza los métodos default de la interfaz del Vehículo:

@Override
public String encenderAlarma() {
    return Vehiculo.super.encenderAlarma();
}

@Override
public String apagarAlarma() {
    return Vehiculo.super.apagarAlarma();
}

De manera similar, podemos hacer que la clase use los métodos default definidos dentro de la interfaz de Alarma:

@Override
public String encenderAlarma() {
    return Alarma.super.encenderAlarma();
}

@Override
public String apagarAlarma() {
    return Alarma.super.apagarAlarma();
}

Métodos static

Además de declarar métodos default en las interfaces, Java 8 también nos permite definir e implementar métodos static en las interfaces.

Dado que los métodos static no pertenecen a un objeto en particular, no son parte de la API de las clases que implementan la interfaz; por lo tanto, deben llamarse utilizando el nombre de la interfaz que precede al nombre del método.

Para comprender cómo funcionan los métodos static en las interfaces, refactoricemos la interfaz del Vehículo y agréguele un método de utilidad static:

public interface Vehiculo {
    
    // ...
    
    static int getPotenciaCaballos(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Definir un método static dentro de una interfaz es idéntico a definir uno en una clase. Además, se puede invocar un método static dentro de otros métodos static y default.

Supongamos que queremos calcular la potencia del motor de un vehículo dado. Simplemente llamamos al método getPotenciaCaballos():

Vehicle.getPotenciaCaballos(2500, 480));

La idea detrás de los métodos static en las interfaces es proporcionar un mecanismo simple que nos permita aumentar el grado de cohesión de un diseño reuniendo métodos relacionados en un solo lugar sin tener que crear un objeto.

Lo mismo se puede hacer prácticamente con las clases abstractas. La principal diferencia es que las clases abstractas pueden tener constructores, estado y comportamiento.

Además, los métodos static en las interfaces permiten agrupar métodos de utilidad relacionados, sin tener que crear clases de utilidad artificiales que son simplemente marcadores de posición para métodos estáticos.