Quelle est la méthode par défaut ?
Après la sortie de Java 8, de nouvelles méthodes peuvent être ajoutées à l'interface, mais l'interface peut toujours rester compatible avec sa classe d'implémentation. Ceci est important car la bibliothèque que vous développez peut être largement utilisée par plusieurs développeurs. Avant Java 8, après la publication d'une interface dans une bibliothèque de classes, si une nouvelle méthode était ajoutée à l'interface, les applications qui implémentaient cette interface risquaient de planter en utilisant la nouvelle version de l'interface.
Avec Java 8, n'y a-t-il pas un tel danger ? La réponse est non.
L'ajout de méthodes par défaut aux interfaces peut rendre certaines classes d'implémentation indisponibles.
Tout d’abord, regardons les détails de la méthode par défaut.
Dans Java 8, les méthodes dans les interfaces peuvent être implémentées (les méthodes statiques dans Java 8 peuvent également être implémentées dans les interfaces, mais c'est un autre sujet). La méthode implémentée dans l'interface est appelée méthode par défaut, qui est identifiée par le mot-clé default comme modificateur. Lorsqu'une classe implémente une interface, elle peut implémenter des méthodes déjà implémentées dans l'interface, mais ce n'est pas obligatoire. Cette classe héritera de la méthode par défaut. C'est pourquoi lorsque l'interface change, la classe d'implémentation n'a pas besoin d'être modifiée.
Qu’en est-il de l’héritage multiple ?
Lorsqu'une classe implémente plus d'une (par exemple deux) interfaces et que ces interfaces ont la même méthode par défaut, les choses deviennent très compliquées. De quelle méthode par défaut la classe hérite-t-elle ? Ni l'un ni l'autre! Dans ce cas, la classe elle-même (directement ou une classe supérieure dans l'arbre d'héritage) doit implémenter la méthode par défaut.
La même chose est vraie lorsqu'une interface implémente la méthode par défaut et qu'une autre interface déclare la méthode par défaut comme abstraite. Java 8 essaie d'éviter toute ambiguïté et de maintenir la rigueur. Si une méthode est déclarée dans plusieurs interfaces, aucune des implémentations par défaut ne sera héritée et vous obtiendrez une erreur de compilation.
Cependant, si vous avez compilé votre classe, il n'y aura aucune erreur de compilation. À ce stade, Java 8 est incohérent. Cela a ses propres raisons, et il y a plusieurs raisons. Je ne veux pas l'expliquer en détail ou en discuter en profondeur ici (car : la version est sortie, le temps de discussion est trop long, et cette plateforme n'a jamais eu une telle discussion).
1. Supposons que vous ayez deux interfaces et une classe d'implémentation.
2. L'une des interfaces implémente une méthode par défaut m().
3. Compilez ensemble l'interface et la classe d'implémentation.
4. Modifiez l'interface qui ne contient pas la méthode m() et déclarez la méthode m() comme abstraite.
5. Recompilez l'interface modifiée séparément.
6. Exécutez la classe d’implémentation.
La classe peut se dérouler normalement dans la situation ci-dessus. Cependant, vous ne pouvez pas recompiler avec l'interface modifiée, mais la compilation avec l'ancienne interface peut toujours s'exécuter. Suivant
1. Modifiez l'interface contenant la méthode abstraite m() et créez une implémentation par défaut.
2. Compilez l'interface modifiée
3. Exécutez la classe : échec.
Lorsque deux interfaces fournissent une implémentation par défaut pour la même méthode, cette méthode ne peut pas être appelée à moins que la classe d'implémentation n'implémente également la méthode par défaut (soit directement, soit plus haut dans la classe de l'arbre d'héritage effectue l'implémentation).
Cependant, cette classe est compatible. Il peut être chargé à l'aide de la nouvelle interface et peut même être exécuté, à condition qu'il n'appelle pas de méthodes ayant des implémentations par défaut dans les deux interfaces.
Exemple de code :
Pour illustrer l'exemple ci-dessus, j'ai créé un répertoire de test pour C.java, là se trouvent également 3 sous-répertoires en dessous, utilisés pour stocker I1.java et I2.java. Le répertoire test contient le code source C.java de classe C. Le répertoire de base contient la version de l'interface qui peut être compilée et exécutée. I1 contient la méthode m() avec une implémentation par défaut et I2 ne contient aucune méthode.
La classe d'implémentation contient la méthode main afin que nous puissions l'exécuter dans le test. Il vérifiera s'il existe des paramètres de ligne de commande, afin que nous puissions facilement effectuer des tests appelant m() et n'appelant pas 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 { }
Utilisez la ligne de commande suivante pour compiler et exécuter :
~/github/test$ javac -cp .:base C.java ~/github/test$ java -cp .:base C hello interface 1
Le répertoire compatible contient l'interface I2 avec la méthode abstraite m() et l'interface I1 non modifiée.
~/github/test$ cat compatible/I2.java public interface I2 { void m(); }
Ceci ne peut pas être utilisé pour compiler la classe 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
Le message d'erreur est très précis. Parce que nous avons la C.class obtenue dans la compilation précédente, si nous compilons les interfaces dans le répertoire compatible, nous obtiendrons toujours deux interfaces pouvant exécuter la classe d'implémentation :
~/github/test$ javac compatible/I*.java ~/github/test$ java -cp .:compatible C hello interface 1
Le troisième répertoire est appelé faux, l'interface I2 incluse définit également la méthode m() :
~/github/test$ cat wrong/I2.java public interface I2 { default void m(){ System.out.println("hello interface 2"); } }
我们应该不厌其烦的编译它。尽管m()方法被定义了两次,但是,实现类仍然可以运行,只要它没有调用那个定义了多次的方法,但是,只要我们调用m()方法,立即就会失败。这是我们使用的命令行参数:
~/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$
结论
当你把给接口添加了default实现的类库移植到Java 8环境下的时候,一般不会有问题。至少Java8类库开发者给集合类添加default方法的时候就是这么想的。使用你类库的应用程序仍然依赖没有default方法的Java7的类库。当使用和修改多个不同的类库的时候,有很小的几率会发生冲突。如何才能避免呢?
像以前那样设计你的类库。可能依赖default方法的时候不要掉以轻心。万不得已不要使用。明智的选择方法名,避免和其它接口产生冲突。我们将会学习到Java编程中如何使用这个特性做开发。
更多Introduction détaillée à la méthode par défaut de Java8相关文章请关注PHP中文网!