[Java] 13. 抽象クラス(abstract)と継承禁止(final)


Study / Java    作成日付 : 2019/08/22 00:06:20   修正日付 : 2021/01/13 18:11:27

こんにちは。明月です。


この投稿はJavaで抽象クラス(abstract)と継承禁止(final)に関する説明です。


私が以前にクラス継承とインタフェース(interface)に関して説明したことがあります。

link - [Java] 8. クラスの継承とthis、superキーワードの使い方

link - [Java] 12. インタフェース(interface)


この抽象クラスはインタフェース(interface)と一般クラスの機能を混ぜていると思えばよいと思います。

インタフェースはクラスの割り当て(new)することはできません。理由は複数継承のため、メモリ構成するメンバー変数がないし、それを処理する関数の処理が実装されてないからです。

クラスはメンバー変数や関数の実行するところまで実装されています。なのでクラスはメモリに割り当てができます。


抽象クラスは基本的に構造は一般クラスと同じです。でも、クラスを生成する時に、直接にインスタンスを生成することではなく、継承する部分まで考えてクラスを作る場合があります。つまり、インタフェースみたいに実行領域は継承されるクラスに任せて定義だけ実装して作成することができます。実行領域がない関数があるので抽象クラスはクラス割り当て、つまりインスタンス生成はできません。または抽象クラスは実行領域があるので複数継承ができません。

抽象クラスの継承はextendsキーワードを使います。

import java.util.ArrayList;
import java.util.List;
// 抽象クラスAbstractClass
abstract class AbstractClass {
  // 関数設定
  public void run() {
    // print()関数を呼び出す。
    print();
  }
  // print()は関数名だけ定義して実装領域は継承されるクラスに任せる。
  protected abstract void print();
}
// AbstractClassを継承した。
class AClass extends AbstractClass {
  // AbstractClassクラスのprint関数は関数名だけ定義しているので再定義しなければならない
  @Override
  protected void print() {
    // コンソール出力
    System.out.println("AClass print!");
  }
}
//AbstractClassを継承した。
class BClass extends AbstractClass {
  // AbstractClassクラスのprint関数は関数名だけ定義しているので再定義しなければならない
  @Override
  protected void print() {
    // コンソール出力
    System.out.println("BClass print!");
  }
}
// 実行クラス
public class Example {
  // 実行関数
  public static void main(String... args) {
    // リスト宣言
    List<AbstractClass> list = new ArrayList<>();
    // AClassインスタンス生成
    list.add(new AClass());
    // BClassインスタンス生成
    list.add(new BClass());
    // リストからクラスを取得
    for (int i = 0; i < list.size(); i++) {
      // インスタンス取得
      AbstractClass clz = list.get(i);
      // run関数実行
      clz.run();
    }
  }
}


上のソースを見ればAbstractClassにprintという関数名だけ定義して実行領域は実装してないです。

AbstractClassを継承したAClassやBClassからprint関数を再定義されます。


上のソースを見てもクラスの属性を持っているしインタフェースの属性も持っていることに見えます。


それなら抽象クラスは複数継承もできないし、一つの完全体のクラスにもありません。そうなれば、抽象クラスを作る意味があるかと疑問があります。

変数タイプで使うみたいにListジェネリックタイプでAbstractClassを使ってAClassとBClassのインスタンスを集まって扱いました。でも、この機能はインタフェースだけでも十分にできる機能です。


でも、実務に行くと抽象クラスはすごくよく使います。インタフェースよりも使います。理由はOOPの特性のカプセル化のせいです。

上の例を見てもmain関数ではAClassのprint関数を直接に呼び出せないです。アクセス修飾子によってアクセスができないです。protectedタイプなので。

AbstractClassのrun関数をよってprint関数が呼び出せるです。クラスを生成する時、カプセル機能で関数を隠せなければならないけど、インタフェースは基本的にpublicなのでできないです。そのため、抽象クラスがよく使います。

// 抽象クラスAbstractClass
abstract class AbstractClass {
  // メンバー変数
  private int data;
  // 実行関数
  public void run() {
    // init関数を呼び出して初期データを受け取る。
    this.data = init();
    // メンバー変数に10を掛ける。
    this.data = this.data * 10;
    // execute関数を呼び出してデータを計算して受け取る。
    this.data = execute(this.data);
    // メンバー変数の10を掛ける。
    this.data = this.data * 10;
    // print関数呼び出す。
    print(this.data);
  }
  // 抽象メソッド定義
  // 初期データを受け取る関数
  protected abstract int init();
  // パラメータのデータを計算して受け取る関数
  protected abstract int execute(int data);
  // データを出力する関数
  protected abstract void print(int data);
}

// AbstractClassを継承した
class AClass extends AbstractClass {
  // init関数を再定義
  @Override
  protected int init() {
    // 1の値をリターンする。
    return 1;
  }
  // execute関数を再定義
  @Override
  protected int execute(int data) {
    // データを5で割る。
    return data / 5;
  }
  // print関数を再定義
  @Override
  protected void print(int data) {
    // データをコンソールに出力
    System.out.println("Data - " + data);
  }
}
// 実行クラス
public class Example {
  // 実行関数
  public static void main(String... args) {
    // AClassクラス宣言
    AClass clz = new AClass();
    // run関数を実行
    clz.run();
  }
}


上の例を見ればAbstractClassはカプセル化でrun関数だけ外部で呼び出すことができます。メンバー変数はprivateタイプだし、処理関数はすべてprotectedタイプです。

AbstractClassを継承するクラスによって結果を変わることができます。 上の例見たいに共通クラスを生成して継承したクラスから業務のプログラムを作成することにすると基本構造は似てますが、クラスによってデータをすべて別に出ると思います。

実務の例を考えばDatabase関連クラスを作ることができます。

Database関連クラスはデータベースを接続してデータを検索するかデータを格納するプロシージャ(実行手順)がありますが、初期のデータベース情報がなければ実行ができません。なので抽象クラスを利用してデータベースは後継承したクラスで設定して優先的に実装することを作業することができます。


抽象クラスは使うことではクラスの前にabstractキーワードが必要です。内部関数ではabstract関数を作成します。abstract関数がなくてもクラス自体がエラーになることではないですが、抽象クラス宣言したことに意味がないです。


今までクラスからクラスを継承することで確認します。でも、クラスをもう継承できないようなキーワードもあります。

// クラス宣言 (class前にfinalキーワードを入れて継承禁止にする。)
final class AClass {
}
// クラス宣言 (AClassを継承する。)
class BClass extends AClass {
}
//実行クラス
public class Example {
  //実行関数
  public static void main(String... args) {

  }
}


BClassはAClassを継承しようと思いましたが、クラスの前にfinalキーワードがあるので継承できませんというコンパイルエラーに表示します。

このfinalキーワードは継承禁止だけではなく、変数を定数に宣言する時も使います。

link - [Java] 2. 変数と定数の宣言方法、そして原始データタイプとクラスデータタイプの差異


ここまでJavaで抽象クラス(abstract)と継承禁止(final)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿