Javaにおける抽象クラス

1概要

契約を実装するときに、実装の一部を延期して後で完了させたい場合が多くあります。これは抽象クラスを介してJavaで簡単に実現できます。

  • このチュートリアルでは、Javaの抽象クラスの基本と、それがどのような場合に役立つかを学びます。

2抽象クラスの主な概念

抽象クラスを使用するタイミングを詳しく説明する前に、最も関連性の高い特性を見てみましょう。

  • ** 前に abstract 修飾子を付けて抽象クラスを定義します。

class キーワード 抽象クラスはサブクラス化することができますが、インスタンス化することはできません

  • クラスが1つ以上の abstract メソッドを定義している場合、そのクラスは

それ自体は abstract と宣言されなければなりません 抽象クラスは抽象メソッドと具象メソッドの両方を宣言できます。

  • 抽象クラスから派生したサブクラスは、すべてを実装する必要があります。

基本クラスの抽象メソッド、または抽象クラスそのもの

これらの概念をよりよく理解するために、簡単な例を作成します。

基本的な抽象クラスにボードゲームの抽象APIを定義させましょう。

public abstract class BoardGame {

   //... field declarations, constructors

    public abstract void play();

   //... concrete methods
}

その後、 _ play _ methodを実装するサブクラスを作成できます。

public class Checkers extends BoardGame {

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

3.抽象クラスを使用する場合

それでは、インターフェイスよりも抽象クラスを優先し、具体的なクラスを選択する必要がある、いくつかの典型的なシナリオを分析しましょう。

  • 共通の機能を一箇所にまとめたい(コード

複数の関連サブクラスが共有する ** サブクラスが簡単にできるAPIを部分的に定義する必要があります。

拡張と改良 ** サブクラスは1つ以上の共通のメソッドまたはフィールドを継承する必要があります。

保護されたアクセス修飾子を使って

これらすべてのシナリオは、https://en.wikipedia.org/wiki/Open%E2%80%93closed__principle[Open/Closed principle]を完全に継承した準拠の良い例です。

さらに、抽象クラスの使用は暗黙的に基本型とサブタイプを扱うため、https://www.baeldung.com/java-polymorphism[Polymorphism]も利用しています。

クラス階層内の「is-a」関係が維持される限り、コードの再利用は抽象クラスを使用する非常に説得力のある理由です。

  • そしてhttps://www.baeldung.com/java-static-default-methods[Java 8ではデフォルトメソッドに別のしわが追加されています]]。

** 4ファイルリーダーの階層例

**

抽象クラスがテーブルにもたらす機能性をより明確に理解するために、別の例を見ましょう。

4.1. 基本抽象クラスの定義

したがって、数種類のファイルリーダーを使用したい場合は、ファイル読み取りに共通するものをカプセル化した抽象クラスを作成します。

public abstract class BaseFileReader {

    protected Path filePath;

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

    public Path getFilePath() {
        return filePath;
    }

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

    protected abstract String mapFileLine(String line);
}

必要に応じてサブクラスがアクセスできるように、 __ filePathを __protectedにしました。さらに重要なことは、** ファイルの内容から実際に1行のテキストを解析する方法です。

私たちの計画は簡単です:私たちの具象クラスはそれぞれファイルパスを保存したりファイルを見たりする特別な方法を持っていませんが、それぞれがそれぞれの行を変換する特別な方法を持つでしょう。

一見すると、 BaseFileReader は不要に思えるかもしれません。しかし、それは、清潔で簡単に拡張可能なデザインの基盤です。これから、 私たちは彼らのユニークなビジネスロジックに焦点を合わせることができるファイルリーダーの異なるバージョンを簡単に実装することができます

4.2. サブクラスの定義

自然な実装はおそらくファイルの内容を小文字に変換するものです:

public class LowercaseFileReader extends BaseFileReader {

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

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

あるいは、ファイルの内容を大文字に変換するものもあります。

public class UppercaseFileReader extends BaseFileReader {

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

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

この単純な例からわかるように、** 各サブクラスは、ファイル読み取りの他の側面を指定する必要なしに、その固有の動作に焦点を合わせることができます。

4.3. サブクラスを使う

最後に、抽象クラスを継承したクラスを使用しても、他の具象クラスと変わりはありません。

@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);
}

簡単にするために、ターゲットファイルは src/main/resources/files フォルダの下にあります。そのため、サンプルファイルのパスを取得するためにアプリケーションクラスローダーを使用しました。 Javaのクラスローダーのチュートリアル をチェックしてください。

5結論

このクイック記事では、Javaでの抽象クラスの基本と、抽象化を実現し、共通の実装を1か所にカプセル化するためにそれらを使用するタイミングについて学びました。

いつもどおり、このチュートリアルに示されているすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/core-java/src/main/java/com/baeldung/abstractclasses[GitHubで利用可能]です。