Java Language Méthodes par défaut


Exemple

Introduites dans Java 8, les méthodes par défaut permettent de spécifier une implémentation dans une interface. Cela pourrait être utilisé pour éviter la classe typique "Base" ou "Abstract" en fournissant une implémentation partielle d'une interface et en limitant la hiérarchie des sous-classes.

Implémentation du modèle d'observateur

Par exemple, il est possible d'implémenter le modèle Observer-Listener directement dans l'interface, offrant plus de flexibilité aux classes d'implémentation.

interface Observer {
    void onAction(String a);
}

interface Observable{
    public abstract List<Observer> getObservers();

    public default void addObserver(Observer o){
        getObservers().add(o);
    }

    public default void notify(String something ){
        for( Observer l : getObservers() ){
            l.onAction(something);
        }
    }
}

Maintenant, n'importe quelle classe peut être rendue "Observable" simplement en implémentant l'interface Observable, tout en étant libre de faire partie d'une hiérarchie de classes différente.

abstract class Worker{
    public abstract void work();
}

public class MyWorker extends Worker implements Observable {

    private List<Observer> myObservers = new ArrayList<Observer>();
    
    @Override
    public List<Observer> getObservers() {
        return myObservers;
    }

    @Override
    public void work(){
        notify("Started work");

        // Code goes here...

        notify("Completed work");
    }
    
    public static void main(String[] args) {    
        MyWorker w = new MyWorker();
       
        w.addListener(new Observer() {
            @Override
            public void onAction(String a) {
                System.out.println(a + " (" + new Date() + ")");
            }
        });
        
        w.work();
    }
}

Problème de diamant

Le compilateur Java 8 est conscient du problème de diamant qui se produit lorsqu'une classe implémente des interfaces contenant une méthode avec la même signature.

Pour le résoudre, une classe d'implémentation doit remplacer la méthode partagée et fournir sa propre implémentation.

interface InterfaceA {
    public default String getName(){
        return "a";
    }
}

interface InterfaceB {
    public default String getName(){
        return "b";
    }
}

public class ImpClass implements InterfaceA, InterfaceB {

    @Override
    public String getName() {    
        //Must provide its own implementation
        return InterfaceA.super.getName() + InterfaceB.super.getName();
    }
    
    public static void main(String[] args) {    
        ImpClass c = new ImpClass();
        
        System.out.println( c.getName() );                   // Prints "ab"
        System.out.println( ((InterfaceA)c).getName() );     // Prints "ab"
        System.out.println( ((InterfaceB)c).getName() );     // Prints "ab"
    }
}

Il y a toujours le problème d'avoir des méthodes avec le même nom et les mêmes paramètres avec des types de retour différents, qui ne compileront pas.

Utiliser les méthodes par défaut pour résoudre les problèmes de compatibilité

Les implémentations de méthodes par défaut sont très pratiques si une méthode est ajoutée à une interface dans un système existant où les interfaces sont utilisées par plusieurs classes.

Pour éviter de casser le système entier, vous pouvez fournir une implémentation de méthode par défaut lorsque vous ajoutez une méthode à une interface. De cette façon, le système continuera à compiler et les implémentations réelles pourront être effectuées étape par étape.


Pour plus d'informations, voir la rubrique Méthodes par défaut .