1. Capacités

Pour mieux comprendre les avantages des interfaces et où les utiliser, nous devons parler de choses plus abstraites.

Une classe modélise généralement un objet particulier. Une interface correspond moins aux objets, et plus à leurs capacités ou rôles.

L'essence des interfaces

Par exemple, des choses comme les voitures, les vélos, les motos et les roues sont mieux représentées en tant que classes et objets. Mais leurs capacités - telles que "je peux être monté", "je peux transporter des gens", "je peux me tenir debout" - sont mieux présentées comme des interfaces. Voici quelques exemples:

Code Description
interface CanMove
{
   void move(String newLocation);
}
Correspond à la capacité de se déplacer
interface Rideable
{
   void ride(Passenger passenger);
}
Correspond à l'aptitude à être monté
interface CanTransport
{
   void addStuff(Object stuff);
   Object removeStuff();
}
Correspond à la capacité de transporter des choses
class Wheel implements CanMove
{
   ...
}
La Wheelclasse peut bouger
class Car implements CanMove, Rideable, CanTransport
{
   ...
}
La Carclasse peut se déplacer, être montée et transporter des choses
class Skateboard implements CanMove, Rideable
{
   ...
}
La Skateboardclasse peut bouger et être montée


2. Rôles

Les interfaces simplifient grandement la vie d'un programmeur. Très souvent, un programme a des milliers d'objets, des centaines de classes, mais seulement quelques dizaines d'interfaces , c'est-à-dire des rôles . Il y a peu de rôles, mais il existe de nombreuses façons de les combiner (classes).

L'intérêt est que vous n'avez pas à écrire de code dans chaque classe pour interagir avec toutes les autres classes. Vous avez juste besoin d'interagir avec leurs rôles (interfaces).

Imaginez que vous êtes un dresseur d'animaux. Chacun des animaux avec lesquels vous travaillez peut avoir plusieurs capacités différentes. Vous vous disputez amicalement avec votre voisin pour savoir quel animal de compagnie peut faire le plus de bruit. Pour régler le problème, il vous suffit d'aligner tous les animaux qui peuvent "parler", et vous leur donnez l'ordre : Parlez !

Vous ne vous souciez pas du genre d'animal qu'ils sont ou de leurs autres capacités. Même s'ils peuvent faire un triple saut périlleux arrière. A ce moment précis, seule leur capacité à parler fort vous intéresse. Voici à quoi cela ressemblerait dans le code :

Code Description
interface CanSpeak
{
   void speak();
}
La CanSpeakcapacité. Cette interface comprend la commande to speak, ce qui signifie qu'elle a une méthode correspondante.
class Cat implements CanSpeak
{
   void speak()
   {
      println("MEOW");
   }
}

class Dog implements CanSpeak
{
   void speak()
   {
      println("WOOF");
   }
}

class Fish
{
   ...
}
Les animaux qui ont cette caractéristique.

Pour faciliter la compréhension, nous avons fourni les noms des classes en anglais. Ceci est autorisé en Java, mais il est hautement indésirable.













Notre Fishn'a pas la capacité de parler (n'implémente pas l' CanSpeakinterface).

public static void main(String[] args)
{
   // Add all the animals to the list
   ArrayList pets = new ArrayList();
   pets.add(new Cat());
   pets.add(new Dog());
   pets.add(new Fish());

   // If the ability exists, then make a sound
   for(Object pet: pets)
   {
      if (pet instanceof CanSpeak)
      {
         CanSpeak loudmouth = (CanSpeak) pet;
         loudmouth.speak();
      }
   }
}
Et comment leur donnons-nous la commande ?

Lorsque le nombre de classes dans vos programmes atteindra les milliers, vous ne pourrez plus vivre sans interfaces. Au lieu de décrire l'interaction de milliers de classes, il suffit de décrire l'interaction de quelques dizaines d'interfaces — cela simplifie grandement la vie.

Et lorsqu'elle est combinée avec le polymorphisme, cette approche est généralement un succès retentissant.



3. La defaultmise en œuvre des méthodes d'interface

Les classes abstraites peuvent avoir des variables et des implémentations de méthodes, mais elles ne peuvent pas avoir d'héritage multiple. Les interfaces ne peuvent pas avoir de variables ou d'implémentations de méthodes, mais cela peut avoir plusieurs héritages.

La situation est exprimée dans le tableau suivant :

Capacité/propriété Cours abstraits Interfaces
variables
Mise en œuvre de la méthode
Héritage multiple

Ainsi, certains programmeurs voulaient vraiment que les interfaces aient la capacité d'avoir des implémentations de méthodes. Mais avoir la possibilité d'ajouter une implémentation de méthode ne signifie pas qu'il en sera toujours ajouté une. Ajoutez-le si vous le souhaitez. Ou si vous ne le faites pas, alors ne le faites pas.

De plus, les problèmes d'héritage multiple sont principalement dus aux variables. En tout cas, c'est ce qu'ils ont décidé et fait. À partir de JDK 8, Java a introduit la possibilité d'ajouter des implémentations de méthode aux interfaces.

Voici un tableau mis à jour (pour JDK 8 et supérieur):

Capacité/propriété Cours abstraits Interfaces
variables
Mise en œuvre de la méthode
Héritage multiple

Désormais, pour les classes abstraites ainsi que pour les interfaces, vous pouvez déclarer des méthodes avec ou sans implémentation. Et c'est une excellente nouvelle !

Dans les classes abstraites, les méthodes sans implémentation doivent être précédées du abstractmot clé. Vous n'avez pas besoin d'ajouter quoi que ce soit avant les méthodes avec une implémentation. Dans les interfaces, le contraire est vrai. Si une méthode n'a pas d'implémentation, rien ne doit être ajouté. Mais s'il y a une implémentation, alors le defaultmot-clé doit être ajouté.

Pour simplifier, nous présentons ces informations dans le petit tableau suivant :

Capacité/propriété Cours abstraits Interfaces
Méthodes sans implémentation abstract
Méthodes avec une implémentation default

Problème

L'utilisation d'interfaces dotées de méthodes peut grandement simplifier les grandes hiérarchies de classes. Par exemple, l'abstract InputStreamet OutputStreamles classes peuvent être déclarés comme des interfaces ! Cela nous permet de les utiliser beaucoup plus souvent et beaucoup plus facilement.

Mais il existe déjà des dizaines de millions (milliards ?) de classes Java dans le monde. Et si vous commencez à changer les bibliothèques standard, vous risquez de casser quelque chose. Comme tout! 😛

Afin de ne pas casser accidentellement les programmes et bibliothèques existants, il a été décidé que les implémentations de méthodes dans les interfaces auraient la priorité d'héritage la plus faible .

Par exemple, si une interface hérite d'une autre interface qui a une méthode et que la première interface déclare la même méthode mais sans implémentation, l'implémentation de la méthode à partir de l'interface héritée n'atteindra pas l'interface héritée. Exemple:

interface Pet
{
   default void meow()
   {
      System.out.println("Meow");
   }
}

interface Cat extends Pet
{
   void meow(); // Here we override the default implementation by omitting an implementation
}

class Tom implements Cat
{
}

Le code ne compilera pas car la Tomclasse n'implémente pas la meow()méthode.