JavaRush /Blog Java /Random-FR /Pause café #140. Classes abstraites et interfaces en Java...

Pause café #140. Classes abstraites et interfaces en Java

Publié dans le groupe Random-FR
Source : InfoWorld Aujourd'hui, vous apprendrez dans quels cas un développeur doit utiliser une classe abstraite et dans quels cas une interface. Nous identifierons également les différences entre ces éléments du langage Java et comment les utiliser dans les programmes. Pause café #140.  Classes abstraites et interfaces en Java - 1Les classes et interfaces abstraites sont assez courantes dans le code Java et même dans le Java Development Kit (JDK) lui-même. Chacun de ces éléments répond à un objectif différent :
  • Une interface est une construction en langage Java qui permet d'implémenter des méthodes abstraites et des constantes statiques.
  • Les classes abstraites sont similaires aux classes normales, sauf qu'elles peuvent inclure des méthodes abstraites, c'est-à-dire des méthodes sans corps. Les classes abstraites ne peuvent pas être créées.
De nombreux développeurs pensent que les interfaces et les classes abstraites sont similaires, mais en réalité ce n'est pas tout à fait vrai. Examinons les principales différences entre eux.

Qu'est-ce qu'une interface

À la base, une interface est un contrat, elle dépend donc de l'implémentation pour définir le but de sa création. L'interface ne peut pas utiliser de variables d'instance mutables, elle ne peut utiliser que des variables finales.

Quand utiliser les interfaces

Les interfaces sont très utiles pour séparer le code et implémenter le polymorphisme. On peut voir cela dans le JDK avec l' interface List :
public interface List<E> extends Collection<E> {

    int size();
    boolean isEmpty();
    boolean add(E e);
    E remove(int index);
    void clear();
}
Comme vous l'avez probablement remarqué, ce code, bien que bref, est assez descriptif. Nous pouvons facilement voir la signature de méthode qui sera utilisée pour implémenter les méthodes dans l'interface en utilisant la classe concrète. L' interface List contient un contrat qui peut être implémenté par les classes ArrayList , Vector , LinkedList et d'autres. Pour utiliser le polymorphisme, nous pouvons simplement déclarer le type de notre variable à l'aide de List , puis sélectionner l'une des instances disponibles. Voici un autre exemple :
List list = new ArrayList();
System.out.println(list.getClass());

 List list = new LinkedList();
 System.out.println(list.getClass());
Le résultat est :
classe java.util.ArrayList classe java.util.LinkedList
Dans ce cas, les méthodes d'implémentation pour ArrayList , LinkedList et Vector sont différentes, ce qui constitue un excellent scénario pour utiliser l'interface. Si vous remarquez que de nombreuses classes appartiennent à une classe parent avec les mêmes actions de méthode mais un comportement différent. Dans de telles situations, il est recommandé d'utiliser l'interface. Examinons ensuite plusieurs options d'utilisation des interfaces.

Remplacement de la méthode d'interface

Comme nous le savons déjà, une interface est une sorte de contrat qui doit être implémenté par une classe concrète. Les méthodes d'interface sont implicitement abstraites et nécessitent une implémentation concrète de la classe. Voici un exemple :
public class OverridingDemo {
  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }
}

interface Challenger {
  void doChallenge();
}

class JavaChallenger implements Challenger {
  @Override
  public void doChallenge() {
    System.out.println("Challenge done!");
  }
}
Conclusion:
Défi réussi !
Notez que les méthodes d'interface sont implicitement abstraites. Cela signifie que nous n’avons pas besoin de les déclarer explicitement abstraits.

Variables constantes

Une autre règle à retenir est qu’une interface ne peut contenir que des variables constantes. Voici un exemple :
public class Challenger {

  int number = 7;
  String name = "Java Challenger";

}
Ici, les deux variables sont implicites final et static . Cela signifie qu'ils sont constants, indépendants de l'instance et ne peuvent pas être modifiés. Nous allons maintenant essayer de changer les variables dans l' interface Challenger , disons comme ceci :
Challenger.number = 8;
Challenger.name = "Another Challenger";
Cela provoquera une erreur de compilation :
Impossible d'attribuer une valeur à la variable finale « numéro » Impossible d'attribuer une valeur à la variable finale « nom »

Méthodes par défaut

Lorsque les méthodes par défaut ont été introduites dans Java 8, certains développeurs pensaient qu'elles seraient identiques aux classes abstraites. Cependant, cela n’est pas vrai car les interfaces ne peuvent pas avoir d’état. Une méthode par défaut peut avoir une implémentation, mais pas les méthodes abstraites. Les méthodes par défaut sont le résultat d'innovations avec les expressions et les flux lambda, mais nous devons les utiliser avec précaution. La méthode du JDK qui utilise la méthode par défaut est forEach() , qui fait partie de l' interface Iterable . Au lieu de copier le code dans chaque implémentation Iterable , nous pouvons simplement réutiliser la méthode forEach :
default void forEach(Consumer<? super T> action) {
  // Code implementation here...
Toute implémentation Iterable peut utiliser la méthode forEach() sans nécessiter une nouvelle implémentation de méthode. On peut alors réutiliser le code avec la méthode par défaut. Créons notre propre méthode par défaut :
public class DefaultMethodExample {

  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }

}

class JavaChallenger implements Challenger { }

interface Challenger {

  default void doChallenge() {
    System.out.println("Challenger doing a challenge!");
  }
}
Résultat:
Challenger faisant un défi !
En ce qui concerne les méthodes par défaut, il est important de noter que chacune de ces méthodes doit être implémentée. La méthode par défaut ne peut pas être statique. Passons maintenant aux classes abstraites.

L'essence d'une classe abstraite

Les classes abstraites peuvent avoir un état avec des variables d'instance. Cela signifie que la variable d'instance peut être utilisée et modifiée. Voici un exemple :
public abstract class AbstractClassMutation {

  private String name = "challenger";

  public static void main(String[] args) {
    AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
    abstractClassMutation.name = "mutated challenger";
    System.out.println(abstractClassMutation.name);
  }

}

class AbstractClassImpl extends AbstractClassMutation { }
Conclusion:
challenger muté

Méthodes abstraites dans les classes abstraites

Comme les interfaces, les classes abstraites peuvent avoir des méthodes abstraites. Une méthode abstraite est une méthode sans corps. Contrairement aux interfaces, les méthodes abstraites des classes abstraites doivent être explicitement déclarées abstraites. Voici un exemple:
public abstract class AbstractMethods {

  abstract void doSomething();

}
Et voici une tentative de déclarer une méthode sans implémentation et sans le mot-clé abstract :
public abstract class AbstractMethods {
   void doSomethingElse();
}
Malheureusement, cela entraîne une erreur de compilation :
Corps de méthode manquant ou déclarer un résumé

Quand utiliser des classes abstraites

Une classe abstraite est recommandée lorsque vous devez implémenter un état mutable. Par exemple, Java Collections Framework inclut une classe AbstractList qui utilise l'état des variables. Dans les cas où vous n'avez pas besoin de conserver l'état de la classe, il est généralement préférable d'utiliser une interface.

Différences entre les classes abstraites et les interfaces

Du point de vue de la programmation orientée objet, la principale différence entre une interface et une classe abstraite est qu'une interface ne peut pas avoir d'état, alors qu'une classe abstraite peut avoir un état avec des variables d'instance. Une autre différence clé est que les classes peuvent implémenter plusieurs interfaces, mais elles ne peuvent étendre qu’une seule classe abstraite. Cette solution est basée sur le fait que l'héritage multiple (étendant plusieurs classes) peut conduire à un blocage du code. Les développeurs du langage Java ont décidé d'éviter cela. Une autre différence est que les interfaces peuvent être implémentées par des classes ou étendues par des interfaces, mais les classes ne peuvent être étendues. Il est important de noter que les expressions lambda ne peuvent être utilisées qu'avec une interface fonctionnelle (c'est-à-dire une interface avec une seule méthode), tandis que les classes abstraites avec une seule méthode abstraite ne peuvent pas utiliser d'expressions lambda. Voici quelques différences supplémentaires entre les classes abstraites et les interfaces. Interface:
  • Ne peut avoir que des variables statiques finales. Une interface ne peut jamais changer son propre état.
  • Une classe peut implémenter plusieurs interfaces.
  • Peut être implémenté à l’aide du mot-clé Implements. Une interface peut étendre une autre interface.
  • Les méthodes ne peuvent utiliser que des champs finaux statiques, des paramètres ou des variables locales.
  • Seules les interfaces fonctionnelles peuvent utiliser la fonction lambda en Java.
  • Impossible d'avoir un constructeur.
  • Peut avoir des méthodes abstraites.
  • Peut avoir des méthodes par défaut et statiques (introduites dans Java 8).
  • Peut avoir des méthodes privées avec implémentation (introduites dans Java 9).
Cours abstraits :
  • Peut avoir n’importe quelle instance ou variable statique, mutable ou immuable.
  • Une classe ne peut étendre qu’une seule classe abstraite.
  • Peut avoir une instance de champs mutables, de paramètres ou de variables locales.
  • Les classes abstraites avec une seule méthode abstraite ne peuvent pas utiliser d'expressions lambda.
  • Peut avoir un constructeur.
  • Peut avoir n’importe quelle méthode.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION