Javaにおける抽象クラス

Javaの抽象クラス

1. 概要

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

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

2. 抽象クラスの重要な概念

抽象クラスをいつ使用するかを説明する前に、let’s look at their most relevant characteristics

  • classキーワードの前にabstract修飾子を付けて抽象クラスを定義します

  • 抽象クラスはサブクラス化できますが、インスタンス化することはできません

  • クラスが1つ以上のabstractメソッドを定義する場合、クラス自体をabstractとして宣言する必要があります

  • 抽象クラスは、抽象メソッドと具象メソッドの両方を宣言できます

  • 抽象クラスから派生したサブクラスは、すべての基本クラスの抽象メソッドを実装するか、それ自体が抽象である必要があります

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

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

public abstract class BoardGame {

    //... field declarations, constructors

    public abstract void play();

    //... concrete methods
}

次に、play メソッドを実装するサブクラスを作成できます。

public class Checkers extends BoardGame {

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

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

さて、let’s analyze a few typical scenarios where we should prefer abstract classes over interfacesと具象クラス:

  • 関連する複数のサブクラスが共有する共通の機能を1か所にカプセル化する(コードの再利用)

  • サブクラスが簡単に拡張および改良できるAPIを部分的に定義する必要があります

  • サブクラスは、保護されたアクセス修飾子を持つ1つ以上の一般的なメソッドまたはフィールドを継承する必要があります

これらのシナリオはすべて、Open/Closed principleへの完全な継承ベースの順守の良い例であることに注意してください。

さらに、抽象クラスの使用は暗黙的に基本タイプとサブタイプを処理するため、Polymorphismも利用しています。

クラス階層内の「is-a」関係が維持されている限り、コードの再利用は抽象クラスを使用する非常に魅力的な理由であることに注意してください。

また、Java 8 adds another wrinkle with default methodsは、抽象クラスを完全に作成する必要がある代わりになる場合があります。

4. ファイルリーダーのサンプル階層

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

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

したがって、いくつかのタイプのファイルリーダーが必要な場合は、ファイルの読み取りに共通するものをカプセル化する抽象クラスを作成できます。

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

必要に応じてサブクラスがアクセスできるように、filePath protectedを作成したことに注意してください。 さらに重要なのは、ファイルの内容からのwe’ve left something undone: how to actually parse a line of textです。

計画は単純です。具体的なクラスには、ファイルパスを保存したり、ファイルをウォークスルーしたりする特別な方法はありませんが、各行を変換する特別な方法があります。

一見すると、BaseFileReaderは不要に見えるかもしれません。 ただし、これはクリーンで簡単に拡張できる設計の基盤です。 そこから、we can easily implement different versions of a file reader that can focus on their unique business logic

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

この簡単な例からわかるように、each subclass can focus on its unique behaviorは、ファイル読み取りの他の側面を指定する必要はありません。

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フォルダーの下にあります。 そのため、サンプルファイルのパスを取得するためにアプリケーションクラスローダーを使用しました。 our tutorial on class loaders in Javaをお気軽にチェックしてください。

5. 結論

この簡単な記事では、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

いつものように、このチュートリアルに示されているすべてのコードサンプルはover on GitHubで利用できます。