JavaRush /Blog Java /Random-ES /Métodos predeterminados en Java 8: ¿qué pueden y qué no p...
Spitfire
Nivel 33

Métodos predeterminados en Java 8: ¿qué pueden y qué no pueden hacer?

Publicado en el grupo Random-ES
Traducción de un artículo escrito por Peter Verhas con fecha de abril de 2014. Métodos predeterminados en Java 8: ¿qué pueden y qué no pueden hacer?  - 1Del traductor: el término " método predeterminado " acaba de aparecer en Java y no estoy seguro de si existe una traducción establecida al ruso para él. Usaré el término "método predeterminado", aunque no creo que sea ideal. Los invito a discutir una traducción más exitosa.

¿Cuál es el método predeterminado?

Ahora, con el lanzamiento de Java 8, puedes agregar nuevos métodos a las interfaces para que la interfaz siga siendo compatible con las clases que la implementan. Esto es muy importante si está desarrollando una biblioteca que utilizan muchos programadores desde Kiev hasta Nueva York. Antes de Java 8, si definía una interfaz en una biblioteca, no podía agregarle métodos sin correr el riesgo de que alguna aplicación que ejecutaba su interfaz se rompiera cuando se actualizaba. Entonces, ¿en Java 8 ya no puedes tener miedo de esto? No, no puedes. Agregar un método predeterminado a una interfaz puede inutilizar algunas clases. Primero veamos las cosas buenas de los métodos predeterminados. En Java 8, el método se puede implementar directamente en la interfaz. (Ahora también se pueden implementar métodos estáticos en una interfaz, pero esa es otra historia). Un método implementado en una interfaz se denomina método predeterminado y se indica mediante la palabra clave default . Si una clase implementa una interfaz, puede implementar los métodos implementados en la interfaz, pero no está obligado a hacerlo. La clase hereda la implementación predeterminada. Es por esto que no es necesario modificar las clases al cambiar la interfaz que implementan.

¿Herencia múltiple?

Las cosas se complican más si una clase implementa más de una (digamos, dos) interfaces e implementan el mismo método predeterminado. ¿Qué método heredará la clase? La respuesta es ninguna. En este caso, la clase debe implementar el método en sí (ya sea directamente o heredándolo de otra clase). La situación es similar si solo una interfaz tiene un método predeterminado y en la otra el mismo método es abstracto. Java 8 intenta ser disciplinado y evitar situaciones ambiguas. Si los métodos se declaran en más de una interfaz, la clase no hereda ninguna implementación predeterminada; obtendrá un error de compilación. Sin embargo, es posible que no obtenga un error de compilación si su clase ya está compilada. Java 8 no es lo suficientemente robusto a este respecto. Hay razones para esto, que no quiero entrar en discusión (por ejemplo: la versión de Java ya se lanzó y el tiempo para las discusiones ya pasó y, en general, este no es el lugar para ellas).
  • Digamos que tienes dos interfaces y una clase implementa ambas.
  • Una de las interfaces implementa el método predeterminado m().
  • Compilas todas las interfaces y la clase.
  • Puedes cambiar una interfaz que no tiene un método m() declarándola como un método abstracto.
  • Compila solo la interfaz modificada.
  • Comienza la clase.
Métodos predeterminados en Java 8: ¿qué pueden y qué no pueden hacer?  - 2En este caso la clase funciona. No puedes compilarlo con las interfaces actualizadas, pero fue compilado con versiones anteriores y, por lo tanto, funciona. Ahora
  • cambie la interfaz con el método abstracto m() y agregue una implementación predeterminada.
  • Compile la interfaz modificada.
  • Clase de ejecución: error.
Cuando hay dos interfaces que proporcionan una implementación predeterminada de un método, ese método no se puede llamar en una clase a menos que sea implementado por la propia clase (nuevamente, ya sea por sí solo o heredado de otra clase). Métodos predeterminados en Java 8: ¿qué pueden y qué no pueden hacer?  - 3Compatible con clases. Se puede cargar con una interfaz modificada. Incluso puede ejecutarse hasta que se llame a un método que tenga una implementación predeterminada en ambas interfaces.

Código de ejemplo

Métodos predeterminados en Java 8: ¿qué pueden y qué no pueden hacer?  - 4Para demostrar lo anterior, creé un directorio de prueba para la clase C.java y 3 subdirectorios para las interfaces en los archivos I1.java e I2.java. El directorio raíz de la prueba contiene el código fuente de la clase C.java. El directorio base contiene una versión de las interfaces que son adecuadas para ejecución y compilación: la interfaz I1 tiene un método predeterminado m(); La interfaz I2 aún no tiene ningún método. La clase tiene un método mainpara que podamos ejecutarlo para probarlo. Comprueba si hay argumentos en la línea de comando, por lo que podemos ejecutarlo fácilmente con o sin llamar al archivo m().
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
Puede compilar y ejecutar la clase desde la línea de comando.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
El directorio compatible contiene una versión de la interfaz I2 que declara que el método m() es abstracto y también, por razones técnicas, una copia sin modificaciones de I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
Un conjunto de este tipo no se puede utilizar para compilar una clase C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
El mensaje de error es muy preciso. Sin embargo, tenemos C.class de una compilación anterior y, si compilamos las interfaces en el directorio compatible, tendremos dos interfaces que aún se pueden usar para ejecutar la clase:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
El tercer directorio wrongcontiene la versión I2, que también declara el método m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
Ni siquiera tienes que preocuparte por la compilación. Aunque el método se declara dos veces, la clase aún se puede usar y ejecutar hasta que se llame al método m(). Esto es para lo que necesitamos el argumento de la línea de comando:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

Conclusión

Cuando migra su biblioteca a Java 8 y cambia sus interfaces para incluir métodos predeterminados, probablemente no tendrá ningún problema. Al menos, eso es lo que esperan los desarrolladores de la biblioteca Java 8 a medida que agregan funcionalidad. Las aplicaciones que utilizan su biblioteca todavía la utilizan para Java 7, donde no hay métodos predeterminados. Si se utilizan varias bibliotecas juntas, existe la posibilidad de que se produzcan conflictos. ¿Cómo evitarlo? Diseñe la API de su biblioteca de la misma manera que antes. No se vuelva complaciente confiando en las capacidades de los métodos predeterminados. Son un último recurso. Elija los nombres con cuidado para evitar colisiones con otras interfaces. Veamos cómo se desarrollará el desarrollo para Java utilizando esta característica.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION