Chapter 12

Java クラスの継承

おでん
おでん
2021.08.18に更新

クラスの継承

クラスのメリットは、1つのクラスからいろんなものを作ることができることです。
例えば、カレーの作り方の設計書(クラス)があるとして、それにチーズを足せばチーズカレーに、とんかつを足せばカツカレーにしたりできます。
この場合、チーズカレーの設計書(クラス)と、カツカレーの設計書(クラス)という2つの新しい設計書(クラス)を簡単に作ることができました。

カレー

あるクラスのメンバ(メンバ変数とメソッド)を拡張して、新しいクラスを定義できることを継承と言います。
継承の元となるものをスーパークラス(親クラス)、継承先のものを**サブクラス(子クラス)**と言います。
上記の例で言うと、カレーのクラスがスーパークラス、チーズカレーとカツカレーのクラスがサブクラスになります。

クラスの継承の記述は以下のように行います。

class サブクラス extends スーパークラス {}

extendsキーワードを使用します。
上記の例で記述してみます。

スーパークラスの定義
class Curry {
    void exShow() {
        System.out.println("カレーの作り方");
    }
}
サブクラスの定義(ここで継承)
class CheeseCurry extends Curry {
    void exCheeseShow() {
        System.out.println("チーズカレーの作り方");
    }
}
Mainクラスの定義
class Main {
    public static void main(String[] args) {
        CheeseCurry ins1 = new CheeseCurry(); //CheeseCurryクラスのインスタンス生成
	ins1.exShow();  //スーパークラスのメソッド呼び出し
	ins1.exCheeseShow();  //サブクラスのメソッド呼び出し
    }
}

これで実行してみます。

$ javac Main.java
$ java Main
カレーの作り方
チーズカレーの作り方

サブクラスからスーパークラス、サブクラス両方のメソッドが実行できています。

継承のルール

継承は複数のクラスから継承すること(多重継承)はできません。
一つのクラスから複数のクラスに継承することは可能です。
また、継承したクラスをさらに継承することも可能です。

オーバーライドとは

オーバーライドとは、スーパークラスで定義されているメソッドを、サブクラス内で再定義することです。
つまり、スーパークラスのメソッドをサブクラス用に、上に重ねて定義することです。(上書きではない)
オーバーライドを定義するには、いくつかの規定があります。(代表的なものは以下)

  • 戻り値の型、メソッド名、引数型、引数の数、引数の並びも同じにする必要がある
  • スーパークラス以上の広い範囲のアクセス修飾子にする必要がある(大体public)

オーバーライドの例です。

ExClass.java
class ExClass {
    public void increase(int i) {  //引数を10倍にするメソッド
        int x = i * 10;
        System.out.println(x);
    }
}
SubExClass.java
class SubExClass extends ExClass {
    public void increase(int i) {  //引数を1000倍にするメソッド
        int x = i * 1000;
        System.out.println(x);
    }
}
ExSubClassMain.java
public class ExSubClassMain {
  public static void main(String[] args){
    //サブクラスのインスタンス化
    SubExClass sub = new SubExClass();
    sub.increase(5);
  }
}
結果
5000

同じ「increase」メソッドでも、スーパークラスでは引数を10倍にするメソッド、サブクラスでは1000倍にするメソッドにオーバーライドしています。
結果、サブクラスでオーバーライドされた引数(5)を1000倍(5000)にするメソッドが実行されています。

スーパークラスのメソッドを明示的に呼び出す

オーバーライドすると、スーパークラスのメソッドを上書きしますが、上書き前のスーパークラスのメソッドも使用したい場合があると思います。
この時は、super.メソッド名を使用します。
上記のオーバーライドの例に追記してみます。

ExClass.java
class ExClass {
    public void increase(int i) {  //引数を10倍にするメソッド
        int x = i * 10;
        System.out.println(x);
    }
}
SubExClass.java
class SubExClass extends ExClass {
    public void increase(int i) {  //引数を1000倍にするメソッド
        int x = i * 1000;
        System.out.println(x);
    }
    public void increase2(int i){   //スーパークラスのメソッドを呼び出すメソッド
	super.increase(i);
      }
}
ExSubClassMain.java
public class ExSubClassMain {
  public static void main(String[] args){
    //サブクラスのインスタンス化
    SubExClass sub = new SubExClass();
    sub.increase(5);
    sub.increase2(5);   //スーパークラスのメソッドを呼び出すメソッドの呼び出し
  }
}
結果
5000
50

サブクラスにて、「super.increase(i)」でスーパークラスの引数を10倍にするメソッドを呼び出しています。
これで、サブクラスでオーバーライドした1000倍にするメソッドと、スーパークラスの10倍にするメソッドの両方を呼び出せています。

final修飾子

final修飾子はメンバやクラスに付与することができます。
それぞれ付与すると以下の効果があります。

  • メンバ変数:定数になる
  • メソッド:オーバーライドできなくなる
  • クラス:継承できなくなる

メンバ変数:定数になる

final修飾子をメンバ変数に付与すると、メンバ変数を再代入できない定数にすることができます。

final int num = 100;
num = 200;  //コンパイルエラー

上記の例だと、2行目で「num」に再代入しようとしていますが、final修飾子の付いているメンバ変数のため、コンパイルエラーとなります。

メソッド:オーバーライドできなくなる

final修飾子をメソッドに付与すると、サブクラスでオーバーライドができなくなります。

Super.java
class Super {
   final void method(){};
}
Sub.java
class Sub extends Super {
   void method(){};  //コンパイルエラー
}

上記の例だと、SubクラスはSuperクラスを継承し、「method」メソッドをオーバーライドしようとしていますが、final修飾子のついているメソッドのため、コンパイルエラーとなります。

クラス:継承できなくなる

final修飾子をクラスに付与すると、サブクラスで継承ができなくなります。

Super.java
final class Super {}
Sub.java
class Sub extends Super {}  //コンパイルエラー

上記の例だと、SubクラスはSuperクラスを継承しようとしていますが、final修飾子のついているクラスのため、コンパイルエラーとなります。