Classes abstraites en Java

Classes abstraites en Java

1. Vue d'ensemble

Lors de la mise en œuvre d'un contrat, il existe de nombreux cas dans lesquels nous souhaitons reporter certaines parties de la mise en œuvre à une exécution ultérieure. Nous pouvons facilement accomplir cela en Java via des classes abstraites.

In this tutorial, we’ll learn the basics of abstract classes in Java, and in what cases they can be helpful.

2. Concepts clés pour les classes abstraites

Avant de vous demander quand utiliser une classe abstraite,let’s look at their most relevant characteristics:

  • On définit une classe abstraite avec le modificateurabstract précédant le mot cléclass

  • Une classe abstraite peut être sous-classée, mais elle ne peut pas être instanciée

  • Si une classe définit une ou plusieurs méthodesabstract, alors la classe elle-même doit être déclaréeabstract

  • Une classe abstraite peut déclarer des méthodes abstraites et concrètes

  • Une sous-classe dérivée d'une classe abstraite doit soit implémenter toutes les méthodes abstraites de la classe de base, soit être abstraite elle-même

Pour mieux comprendre ces concepts, nous allons créer un exemple simple.

Laissez notre classe abstraite de base définir l'API abstraite d'un jeu de société:

public abstract class BoardGame {

    //... field declarations, constructors

    public abstract void play();

    //... concrete methods
}

Ensuite, nous pouvons créer une sous-classe qui implémente la méthodeplay :

public class Checkers extends BoardGame {

    public void play() {
        //... implementation
    }
}

3. Quand utiliser les classes abstraites

Maintenant,let’s analyze a few typical scenarios where we should prefer abstract classes over interfaces et classes concrètes:

  • Nous voulons encapsuler certaines fonctionnalités communes à un endroit (réutilisation de code) que plusieurs sous-classes liées partageront.

  • Nous devons définir partiellement une API que nos sous-classes peuvent facilement étendre et affiner.

  • Les sous-classes doivent hériter d'une ou plusieurs méthodes ou champs communs avec des modificateurs d'accès protégés

Gardons à l'esprit que tous ces scénarios sont de bons exemples d'adhésion complète, basée sur l'héritage, auxOpen/Closed principle.

De plus, étant donné que l’utilisation de classes abstraites traite implicitement des types de base et des sous-types, nous tirons également parti dePolymorphism.

Notez que la réutilisation de code est une raison très convaincante d'utiliser des classes abstraites, tant que la relation «is-a» au sein de la hiérarchie des classes est préservée.

EtJava 8 adds another wrinkle with default methods, qui peut parfois remplacer le besoin de créer complètement une classe abstraite.

4. Un exemple de hiérarchie de lecteurs de fichiers

Pour comprendre plus clairement les fonctionnalités que les classes abstraites apportent à la table, examinons un autre exemple.

4.1. Définition d'une classe abstraite de base

Donc, si nous voulions avoir plusieurs types de lecteurs de fichiers, nous pourrions créer une classe abstraite qui encapsule ce qui est commun à la lecture de fichiers:

public abstract class BaseFileReader {

    protected Path filePath;

    protected BaseFileReader(Path filePath) {
        this.filePath = filePath;
    }

    public Path getFilePath() {
        return filePath;
    }

    public List readFile() throws IOException {
        return Files.lines(filePath)
          .map(this::mapFileLine).collect(Collectors.toList());
    }

    protected abstract String mapFileLine(String line);
}

Notez que nous avons crééfilePath protégé pour que les sous-classes puissent y accéder si nécessaire. Plus important encore,we’ve left something undone: how to actually parse a line of text du contenu du fichier.

Notre plan est simple: alors que nos classes concrètes n’ont pas chacune un moyen spécial de stocker le chemin du fichier ou de parcourir le fichier, elles auront chacune une façon spéciale de transformer chaque ligne.

À première vue,BaseFileReader peut sembler inutile. Cependant, c'est la base d'une conception propre et facilement extensible. À partir de là,we can easily implement different versions of a file reader that can focus on their unique business logic.

4.2. Définition des sous-classes

Une implémentation naturelle est probablement celle qui convertit le contenu d'un fichier en minuscules:

public class LowercaseFileReader extends BaseFileReader {

    public LowercaseFileReader(Path filePath) {
        super(filePath);
    }

    @Override
    public String mapFileLine(String line) {
        return line.toLowerCase();
    }
}

Ou un autre peut être celui qui convertit le contenu d'un fichier en majuscules:

public class UppercaseFileReader extends BaseFileReader {

    public UppercaseFileReader(Path filePath) {
        super(filePath);
    }

    @Override
    public String mapFileLine(String line) {
        return line.toUpperCase();
    }
}

Comme nous pouvons le voir dans cet exemple simple,each subclass can focus on its unique behavior sans avoir besoin de spécifier d'autres aspects de la lecture de fichiers.

4.3. Utilisation d'une sous-classe

Enfin, utiliser une classe qui hérite d’une classe abstraite n’est pas différent de toute autre classe concrète:

@Test
public void givenLowercaseFileReaderInstance_whenCalledreadFile_thenCorrect() throws Exception {
    URL location = getClass().getClassLoader().getResource("files/test.txt")
    Path path = Paths.get(location.toURI());
    BaseFileReader lowercaseFileReader = new LowercaseFileReader(path);

    assertThat(lowercaseFileReader.readFile()).isInstanceOf(List.class);
}

Par souci de simplicité, le fichier cible se trouve sous le dossiersrc/main/resources/files. Par conséquent, nous avons utilisé un chargeur de classe d’application pour obtenir le chemin du fichier exemple. N'hésitez pas à consulterour tutorial on class loaders in Java.

5. Conclusion

Dans cet article rapide,we learned the basics of abstract classes in Java, and when to use them for achieving abstraction and encapsulating common implementation in one single place.

Comme d'habitude, tous les exemples de code présentés dans ce tutoriel sont disponiblesover on GitHub.