概要
クラスの継承とは、継承元(親)のフィールド(値)、メソッド(機能)を継承先(子)に引き継がせることです。
継承先(子)では、追加のフィールド(値)、メソッド(機能)を持つことができます。
つまり、以下のようになります。
・継承元(親):継承先(子)に共通のフィールド、メソッドを持たせる
・継承先(子):継承元(親)に加えて、独自のフィールド、メソッドを持たせることができる
オーバライド
オーバライドには以下の条件を満たす必要があります。
・シグネチャが同じであること
・戻り値が同じ型である、またはサブクラスであること
・アクセス修飾子は同じである、またはより緩いこと
シグネチャが同じであること
シグネチャとは、メソッド名、引数の型、引数の数で構成されるひとまとまりのことです。
以下でコードで事例を交えて説明しておりますので、言葉だけではよくわからないという方は参照下さい。
⇒【Java入門】メソッドとは?使い方まとめ #シグネチャとは?
戻り値が同じ型である、またはサブクラスであること
戻り値の型にはサブクラスも指定できます。
親クラスで定義されているメソッドの戻り値の型がListだったら、子クラスでオーバライドした際にはArrayListも指定できるということになります。
(List、ArrayListは比較的メジャーなスーパークラス – サブクラスの関係なので、ピンときやすいかなと思って例に挙げました)
アクセス修飾子は同じである、またはより緩いこと
アクセス修飾子の強弱関係は以下のようになります。(左が最も緩い)
public > protected > パッケージプライべート > private
同じかより緩くすることしかできないので、親クラスでの定義がpublicであれば他は指定できません。
逆にprivateであれば、いずれでも指定可能です。
型変換
アップキャスト
子クラスの型を親クラスの型に変換します。
子クラスには親クラスをextendsしているということがコードに書かれているので、コンパイラは互換性があると判断できます。
なのでアップキャストの場合は、(型名)といったキャスト式が無くても変換は自動で行われます。
ダウンキャスト
親クラスの型を子クラスの型に変換します。
親クラスに子クラスのインスタンスを入れたとしても、コンパイラはインスタンスを見て判断するのではなく、型を見て判断するため、親クラスからは子クラスに対する互換性があるかが判断できません。
その場合、キャスト式(型名)を付けて明示的に変換します。
図解
java.lang.Numberの親子関係での例示です。
Number型を抽象クラスとして、各具象クラスが継承関係にあります。
Javaでは継承可能な親は1つだけと決まっているので、親の方に戻す時に型指定は必要ないと認識した方がわかりやすいでしょう。
そして、子クラスはいくつでも継承させられるので何に変換されたかかわかるように、親⇒子へのキャストは明示的である必要があります。
コード例
親クラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public abstract class AbstractClass { protected int num; protected String name; public AbstractClass(int num, String name) { this.num = num; this.name = name; } // 親クラス独自のメソッド public void text() { System.out.println(">> AbstractClass"); System.out.println("num = " + num + ", name = " + name); } // 親クラスで定義した抽象メソッド protected abstract void extendText(); } |
子クラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class ConcreteClass extends AbstractClass { private boolean flg; public ConcreteClass(int num, String name, boolean flg) { super(num, name); this.flg = flg; } // 親クラスの抽象メソッドをオーバライド @Override public void extendText() { System.out.println(">> ConcreteClass"); System.out.println("num = " + num + ", name = " + name + ", flg = " + flg); } // 子クラス独自のメソッド public void notExtendText() { System.out.println(">> Only ConcreteClass can call notExtendText()."); } } |
実行クラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class ConcreteMain { public static void main(String[] args) { // 親クラスの型でインスタンスを生成 AbstractClass ac = new ConcreteClass(10, "Type is AbstractClass", true); ac.text(); ac.extendText(); // ac.notExtendText(); // コンパイルエラーになる System.out.println(); // 子クラスの型でインスタンスを生成 ConcreteClass cc = new ConcreteClass(20, "Type is ConcreteClass", true); cc.text(); cc.extendText(); cc.notExtendText(); System.out.println(); // 親クラスにアップキャストは自動で行われる AbstractClass upCast = cc; upCast.text(); System.out.println(); // 子クラスにダウンキャストは明示的に指定が必要 ConcreteClass downCast = (ConcreteClass)upCast; downCast.text(); } } |
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 親クラスの型でインスタンスを生成 >> AbstractClass num = 10, name = Type is AbstractClass >> ConcreteClass num = 10, name = Type is AbstractClass, flg = true // 子クラスの型でインスタンスを生成 >> AbstractClass num = 20, name = Type is ConcreteClass >> ConcreteClass num = 20, name = Type is ConcreteClass, flg = true >> Only ConcreteClass can call notExtendText(). // 親クラスにアップキャストは自動で行われる >> AbstractClass num = 20, name = Type is ConcreteClass // 子クラスにダウンキャストは明示的に指定が必要 >> AbstractClass num = 20, name = Type is ConcreteClass |
まとめ
- 継承によって親クラスを引き継いだ子クラスを定義できる。
- オーバライドによって親クラスのメソッドを上書きできる。
- 親クラスへのキャストは自動的に行われるが、子クラスへのキャストは明示的に指定する必要がある。
コメント