Dans Java, les méthodes par défaut (default methods) ont été introduites avec JDK 1.8 pour permettre l'extension des interfaces sans causer de problèmes de compatibilité. Avant cela, modifier une interfcae signifiait que toutes les classes implémentant cette interface devaient être mises à jour. Par exemple, l'interface List a ajouté la méthode stream() comme méthode par défaut, ce qui a permis aux bibliothèques tierces de fonctionner sans changement immédiat.
Voici un exemple illustrant l'utilisation d'une méthode par défaut dans une interface :
package com.example.demo;
@FunctionalInterface
public interface Measurable {
int obtenirTaille();
default boolean estVide() {
return obtenirTaille() == 0;
}
}
Ici, l'interface Measurable définit une méthode abstraite obtenirTaille() et une méthode par défaut estVide() qui dépend de la première.
Lorsqu'une classe implémente plusieurs interfaces avec des méthodes par défaut de même nom, des conflits peuvent se produire. La résolution suit ces principes :
Principe 1 : La classe a la priorité absolue. Si la classe redéfinit la méthode, c'est cette version qui est utilisée.
package com.example.demo;
interface Actionnable {
default void demarrer() {
System.out.println("Démarrage depuis Actionnable");
}
}
interface Extensible extends Actionnable {
@Override
default void demarrer() {
System.out.println("Démarrage depuis Extensible");
}
}
class Application implements Actionnable, Extensible {
@Override
public void demarrer() {
System.out.println("Démarrage depuis Application");
}
}
Dans ce code, la méthode de Application est appelée, car la classe redéfinit explicitement la méthode.
Principe 2 : En l'absence de redéfinition dans la classe, l'interface la plus spécifique (sous-interface) est choisei.
package com.example.demo;
interface Affichable {
default void afficher() {
System.out.println("Affichage de Affichable");
}
}
interface Derivable extends Affichable {
@Override
default void afficher() {
System.out.println("Affichage de Derivable");
}
}
class Composant implements Affichable, Derivable {
// Aucune redéfinition, donc la méthode de Derivable est utilisée.
}
La classe Composant utilise la méthode de Derivable car cette interface hérite de Affichable.
Pirncipe 3 : Si les interfaces sont indépendantes et qu'un conflit persiste, la classe doit obligatoirement redéfinir la méthode.
package com.example.demo;
interface TraitementA {
default void executer() {
System.out.println("Exécution A");
}
}
interface TraitementB {
default void executer() {
System.out.println("Exécution B");
}
}
class Processeur implements TraitementA, TraitementB {
@Override
public void executer() {
// On peut optionnellement appeler une super méthode pour combiner les comportements.
TraitementA.super.executer();
System.out.println("Exécution personnalisée");
}
}
Ici, Processeur doit implémenter executer() pour clarifier l'ambiguïté entre les deux interfaces.