Java入門19 クラスの継承、メソッドのオーバーライド

Java入門の第19回です。前回からの続きですが、今回だけでも分かる内容になっています。このシリーズの第1回はこちらです。

今回は、クラスの継承について解説します。クラスの継承とは、親クラスの定義内容をそのまま受け継いで子クラスを作成することです。

クラスの継承とは

クラスの継承とは、親クラスの定義内容をそのまま受け継いで子クラスを作成することです。コピーと似ていますが、継承はコピーとは異なります。

元のクラスをコピー&ペーストして、新しく作成したクラスを書き換えていく場合、元のクラスを書き換えても新しいクラスは書き換わりません。コピー元クラスとコピー先クラスの整合性を維持するには、コピー元クラスを書き換えるたびに、コピー先クラスも同じように書き換えなくてはなりません。

クラスの継承の場合、元となる親クラスを継承して新しい子クラスを作成します。親のすべてを引き継いで子が誕生するため、親クラスで定義した内容を、子クラスで改めて定義し直す必要はありません。また、親クラスの定義を変更すると、子クラスにもその変更が引き継がれます。

継承では、元になる親クラスの持つ機能をすべて受け継いだ子クラスを作成して、そこへ新しい機能を追加して拡張していけます。

クラス継承の書式

クラス継承の書式は、以下の通りです。

class P {
   Pクラスで定義するフィールドやメソッド
}

class C extends P {
   Cクラスで追加で定義するフィールドやメソッド
}

Pクラスが親クラス、Cクラスが子クラスです。クラス継承の際には extends キーワードを使用します。class C extends Pと書けば、「Pクラスを継承して、Cクラスを定義します」という意味になります。

「extends」という英単語は「拡張する」という意味ですが、子クラスは親クラスのすべてを引き継いだ上で、さらに新しいフィールドやメソッドを追加して親クラスから機能拡張させたものです。

尚、親クラスを「スーパークラス」、子クラスを「サブクラス」と呼ぶこともあります。また、「子クラスは親クラスを“継承”したもの」ですが、これと同じ意味で「子クラスは親クラスから“派生”したもの」と呼ぶこともあります。

クラス継承の実例

実例で確認しましょう。以下のコードでは、親クラスを継承して子クラスを定義しています。

//親クラス
class Point {
    int x;
    int y;
}

//子クラス
class Circle extends Point {
    int r;

    void printCircle() {
        System.out.println("円の中心:" + x + "," + y);
        System.out.println("円の半径:" + r);
    }
}

public class Test22 {
    public static void main(String[] args) {
        //インスタンスを生成
        Circle c1 = new Circle();
        c1.x = 3;
        c1.y = 5;
        c1.r = 10;
        //インスタンスメソッドを呼び出す
        c1.printCircle();
    }
}

上記コードでは、親クラスのPointクラスを継承して、子クラスのCircleクラスを定義しています。Circleクラスで定義した変数は、円の半径を表す変数rだけですが、インスタンス変数を代入する際には c1.r だけではなく c1.x と c1.y にも値を代入しています。

これは、CircleクラスがPointクラスを継承しているためです。親クラスを継承した子クラスでは、親クラスで定義されているフィールドやメソッドをあらためて定義しなくてもそのまま利用できます。親のPointクラス で定義されているxやyは、子のCircleクラスでもそのまま利用できます。

上記コードの実行結果は、以下の通りです。

円の中心:3,5
円の半径:10

以下は、上記コードをEclipseで実行した結果画面です。

メソッドをオーバーライドする

親クラスにあるメソッドは、子クラスから上書きすることもできます。これをメソッドの「オーバーライド」と呼びます。メソッドをオーバーライドするには、親クラスで定義されているメソッドと同じ名前で、同じ引数を持つメソッドを定義します。

仮に、以下のようなコードがあるとします。以下のコードを実行すると、親クラスにあるprintZahyo()メソッドが実行されます。

//親クラス
class Point {
    int x;
    int y;

    //親クラスにメソッドがある
    void printZahyo() {
        System.out.println(x + "," + y);
    }
}

//子クラス
class Circle extends Point {
    int r;
}

public class Test22 {
    public static void main(String[] args) {
        //インスタンスを生成
        Circle c1 = new Circle();
        c1.x = 3;
        c1.y = 5;
        c1.r = 10;
        //メソッドを呼び出す
        c1.printZahyo();
    }
}

上記コードの実行結果は、以下の通りです。

3,5

次に、メソッドをオーバーライドする実例を見てみましょう。以下のコードでは、親クラスにあるのと同じ名前のprintZahyo()メソッドを子クラスでも定義しています。この場合、子クラスで定義したメソッドが親クラスのメソッドをオーバーライド(上書き)します。

//親クラス
class Point {
    int x;
    int y;

    //親クラスにメソッドがある
    void printZahyo() {
        System.out.println(x + "," + y);
    }
}

//子クラス
class Circle extends Point {
    int r;

    //子クラスにも同じ名前のメソッドがある(こちらが優先される)
    void printZahyo() {
        System.out.println("円の中心:" + x + "," + y);
        System.out.println("円の半径:" + r);
    }
}

public class Test22 {
    public static void main(String[] args) {
        //インスタンスを生成
        Circle c1 = new Circle();
        c1.x = 3;
        c1.y = 5;
        c1.r = 10;
        //メソッドを呼び出す
        c1.printZahyo();
    }
}

上記コードの実行結果は、以下の通りです。

円の中心:3,5
円の半径:10

上の実例の通り、親クラスと子クラスに同じ名前、同じ引数のメソッドが重複して存在している場合には、子クラスで定義したメソッドが優先されてオーバーライドされます。

オーバーロードとオーバーライド

前回登場したオーバーロードと、今回登場したオーバーライドは、言葉は似ていますが意味はむしろ反対かもしれません。

「オーバーロード」は同じ名前のメソッドを複数重複して定義した状態ですが、引数により使い分けることができます。

一方、「オーバーライド」は同じ名前のメソッドを複数重複して定義した状態ですが、子クラス側で定義したメソッドで上書きされます。

次回へ続きます。