Méthodes statiques et par défaut dans les interfaces en Java

Méthodes statiques et par défaut dans les interfaces en Java

1. Vue d'ensemble

Java 8 a apporté à la table quelques nouvelles fonctionnalités, notammentlambda expressions,functional interfaces,method references,streams,Optional etstatic et default méthodes dans les interfaces.

Certains d'entre eux ont déjà été couverts enthis article. Néanmoins, les méthodesstatic etdefault dans les interfaces méritent à elles seules un examen plus approfondi.

Dans cet article, nous aborderons en détailhow to use static and default methods in interfaces et passerons en revue quelques cas d'utilisation où ils peuvent être utiles.

2. Pourquoi les méthodes par défaut dans les interfaces sont nécessaires

Comme les méthodes d'interface classiques,default methods are implicitly public - il n'est pas nécessaire de spécifier le modificateurpublic.

Contrairement aux méthodes d'interface régulières, ce sont desdeclared with the default keyword at the beginning of the method signature et desprovide an implementation.

Voyons un exemple simple:

public interface MyInterface {

    // regular interface methods

    default void defaultMethod() {
        // default method implementation
    }
}

La raison pour laquelle les méthodesdefault ont été incluses dans la version Java 8 est assez évidente.

Dans une conception typique basée sur des abstractions, où une interface a une ou plusieurs implémentations, si une ou plusieurs méthodes sont ajoutées à l'interface, toutes les implémentations seront forcées de les implémenter également. Sinon, le design va simplement s'effondrer.

Les méthodes d'interface par défaut sont un moyen efficace de résoudre ce problème. Ilsallow us to add new methods to an interface that are automatically available in the implementations. Ainsi, il n’est pas nécessaire de modifier les classes d’implémentation.

De cette façon,backward compatibility is neatly preserved sans avoir à refactoriser les implémenteurs.

3. Méthodes d'interface par défaut en action

Pour mieux comprendre les fonctionnalités des méthodes d'interface dedefault, créons un exemple simple.

Disons que nous avons une interfaceVehiclenaïve et une seule implémentation. Il pourrait y en avoir plus, mais restons aussi simple:

public interface Vehicle {

    String getBrand();

    String speedUp();

    String slowDown();

    default String turnAlarmOn() {
        return "Turning the vehicle alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the vehicle alarm off.";
    }
}

Et écrivons la classe d'implémentation:

public class Car implements Vehicle {

    private String brand;

    // constructors/getters

    @Override
    public String getBrand() {
        return brand;
    }

    @Override
    public String speedUp() {
        return "The car is speeding up.";
    }

    @Override
    public String slowDown() {
        return "The car is slowing down.";
    }
}

Enfin, définissons une classemain typique, qui crée une instance deCar et appelle ses méthodes:

public static void main(String[] args) {
    Vehicle car = new Car("BMW");
    System.out.println(car.getBrand());
    System.out.println(car.speedUp());
    System.out.println(car.slowDown());
    System.out.println(car.turnAlarmOn());
    System.out.println(car.turnAlarmOff());
}

Veuillez noter que les méthodesdefaultturnAlarmOn() etturnAlarmOff() de notre interfaceVehicle sontautomatically available in the Car class.

De plus, si à un moment donné nous décidons d'ajouter plus de méthodesdefault à l'interfaceVehicle, l'application continuera à fonctionner, et nous n'aurons pas à forcer la classe à fournir des implémentations pour les nouvelles méthodes .

L'utilisation la plus courante des méthodes par défaut dans les interfaces estto incrementally provide additional functionality to a given type without breaking down the implementing classes.

De plus, ils peuvent être utilisés pourprovide additional functionality around an existing abstract method:

public interface Vehicle {

    // additional interface methods

    double getSpeed();

    default double getSpeedInKMH(double speed) {
       // conversion
    }
}

4. Règles d'héritage d'interfaces multiples

Les méthodes d'interface par défaut sont une fonctionnalité très intéressante, mais avec quelques mises en garde qui méritent d'être mentionnées. Puisque Java permet aux classes d'implémenter plusieurs interfaces, il est important de connaîtrewhat happens when a class implements several interfaces that define the same default methods.

Pour mieux comprendre ce scénario, définissons une nouvelle interfaceAlarm et refactorisons la classeCar:

public interface Alarm {

    default String turnAlarmOn() {
        return "Turning the alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the alarm off.";
    }
}

Avec cette nouvelle interface définissant son propre ensemble de méthodesdefault, la classeCar implémenterait à la foisVehicle etAlarm:

public class Car implements Vehicle, Alarm {
    // ...
}

Dans ce cas,the code simply won’t compile, as there’s a conflict caused by multiple interface inheritance (alias lesDiamond Problem). La classeCar hériterait des deux ensembles de méthodesdefault. Lesquels devraient être appelés alors?

Pour résoudre cette ambiguïté, nous devons explicitement fournir une implémentation pour les méthodes:

@Override
public String turnAlarmOn() {
    // custom implementation
}

@Override
public String turnAlarmOff() {
    // custom implementation
}

On peut aussihave our class use the default methods of one of the interfaces.

Voyons un exemple qui utilise les méthodesdefault de l’interfaceVehicle:

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff();
}

De même, nous pouvons demander à la classe d'utiliser les méthodesdefault définies dans l'interfaceAlarm:

@Override
public String turnAlarmOn() {
    return Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Alarm.super.turnAlarmOff();
}

De plus, c'est mêmepossible to make the Car class use both sets of default methods:

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}

5. Méthodes d'interface statique

En plus de pouvoir déclarer les méthodesdefault dans les interfaces,Java 8 allows us to define and implement static methods in interfaces.

Puisque les méthodesstatic n'appartiennent pas à un objet particulier, elles ne font pas partie de l'API des classes implémentant l'interface, et elles doivent êtrecalled by using the interface name preceding the method name.

Pour comprendre comment les méthodesstatic fonctionnent dans les interfaces, refactorisons l’interfaceVehicle et ajoutons-y une méthode utilitairestatic:

public interface Vehicle {

    // regular / default interface methods

    static int getHorsePower(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Defining a static method within an interface is identical to defining one in a class. De plus, une méthodestatic peut être invoquée dans d'autres méthodesstatic etdefault.

Maintenant, disons que nous voulons calculer leshorsepower du moteur d’un véhicule donné. Nous appelons simplement la méthodegetHorsePower():

Vehicle.getHorsePower(2500, 480));

L'idée derrière les méthodes d'interfacestatic est de fournir un mécanisme simple qui nous permet deincrease the degree of cohesion d'une conception en rassemblant les méthodes associées en un seul endroit sans avoir à créer un objet.

À peu prèsthe same can be done with abstract classes. La principale différence réside dans le fait queabstract classes can have constructors, state, and behavior.

De plus, les méthodes statiques dans les interfaces permettent de regrouper les méthodes utilitaires associées, sans avoir à créer des classes utilitaires artificielles qui sont simplement des espaces réservés pour les méthodes statiques.

6. Conclusion

Dans cet article, nous avons exploré en profondeur l'utilisation des méthodes d'interfacestatic etdefault dans Java 8. À première vue, cette fonctionnalité peut sembler un peu négligée, en particulier dans une perspective puriste orientée objet. Idéalement, les interfaces ne doivent pas encapsuler le comportement et doivent être utilisées uniquement pour définir l'API publique d'un certain type.

Quand il s'agit de maintenir la compatibilité descendante avec le code existant, cependant, les méthodesstatic etdefault sont un bon compromis.

Et, comme d'habitude, tous les exemples de code présentés dans cet article sont disponiblesover on GitHub.