メソッドのオーバーライド
継承をすると、スーパークラスのメンバをすべて利用することができます。
privateの場合は直接アクセスできませんが、メンバとして持ってはいます。
ここで、スーパークラスが持っているメソッドと同じ名前のメソッドを記述できるでしょうか?
例えば、前回書いたサンプルのクラスSalarymanにselfIntroduce( )というメソッドを記述するとどうなるでしょうか。
結論を言うと、同じ名前、引数のメソッドでもクラスが違えば定義することができます。
その場合、サブクラスのインスタンスはサブクラスに実装されたメソッドを実行します。
スーパークラスに合致するメソッドがあっても、それが呼び出されることはありません。
これを「メソッドのオーバーライド」と言います。
ポイント!メソッドのオーバーライドをする理由
Humanクラスには様々な機能(メソッド)がありますが、すべてが利用者の要求を満たすとは限りません。
そのため、オーバーライドで一部の機能を変更することができます。
オーバーライドしていないメソッドは、今まで通りスーパークラスに実装されたものを呼び出します。
【オーバーライドのイメージ】
【メソッドのオーバーライド】
Humanクラス
public class Human { private String name; private int age; private double height; public Human( ){ System.out.println("Humanの引数なしコンストラクタが呼ばれました!"); } public Human(String n, int a, double h){ this( ); System.out.println("Humanの引数ありコンストラクタが呼ばれました!"); name = n; age = a; height = h; } public void setName(String n){ name = n; } public void setAge(int a){ age = a; } public void setHeight(double h){ height = h; } public String getName( ){ return name; } public int getAge( ){ return age; } public double getHeight( ){ return height; } public void selfIntroduce( ){ System.out.println("私の名前は" + name + "です"); System.out.println("私の年齢は" + age + "です"); System.out.println("私の身長は" + height + "です"); } }
Salarymanクラス
public class Salaryman extends Human{ private int salary; private int bonus; public Salaryman( ){ System.out.println("Salarymanの引数なしコンストラクタが呼ばれました!"); } public Salaryman(String n, int a, double h, int s, int b){ super(n, a, h); System.out.println("Salarymanの引数ありコンストラクタが呼ばれました!"); salary = s; bonus = b; } public void selfIntroduce( ){ System.out.println("私の名前は" + getName( ) + "です"); System.out.println("私の年齢は" + getAge( ) + "です"); System.out.println("私の身長は" + getHeight( ) + "です"); System.out.println("私の年収は" + getIncome( ) + "です"); } public int getIncome( ){ return salary * 12 + bonus * 2; } }
UseHumanクラス
public class UseHuman{ public static void main(String[ ] args){ Salaryman taro = new Salaryman("太郎", 30, 175.5, 30, 60); //Humanクラスのメソッド taro.selfIntroduce( ); //Salarymanクラスのメソッド // int income = taro.getIncome( ); // System.out.println("収入は" + income + "です"); } }
実行結果
Humanの引数なしコンストラクタが呼ばれました! Humanの引数ありコンストラクタが呼ばれました! Salarymanの引数ありコンストラクタが呼ばれました! 私の名前は太郎です 私の年齢は30です 私の身長は175.5です 私の年収は480です
5行目のselfIntroduce( )メソッド呼び出しは、HumanクラスではなくSalarymanクラスのものを呼び出します。
7行目と8行目では、自分で年収を取得して画面に出力していました。
それはSalarymanクラスでやってくれるので不要になります。
main( )メソッドの長さはたったの2行です。
インスタンスを生成してメソッドを呼んだだけですべてのデータが画面に表示されました。
ポイント!オーバーライドとオーバーロード
メソッドのオーバーライドは、オーバーロードと名前がよく似ていて間違えやすいので注意が必要です。
オーバーロード
同一クラス内で、引数が違う、同名のメソッドを定義
オーバーライド
継承したクラスで、引数と名前が同じメソッドを定義
これらに関しては残念ながらちょうど良い訳語がありません。例えばオーバーライドのことを遮蔽定義と言うこともありますが、わかりにくい上に一般的にも知られていません。
言葉に気をつけて使いましょう。
オーバーライドされたメソッドの呼び出し
メソッドをオーバーライドすると、もともとスーパークラスで実装されていたメソッドは隠されて見えなくなってしまいます(呼び出されなくなってしまいます)。
その際にはサブクラスの機能が優先されます。
HumanクラスのselfIntroduce( )メソッドでは年収の出力ができなかったのでSalarymanクラスのselfIntroduce( )メソッドを新しく実装しました。
ところが、HumanクラスのselfIntroduce( )は、それ以外のフィールドを出力する機能は存在しました。
これをわざわざSalarymanクラスで同じことを書くのは良くありません。
メソッドのオーバーライドをするのは、スーパークラスの機能を「変更したい」ときです。
しかし完全に変更するのではなく、「付け足したい」という場合もあります。
例えば今回の例はそれにあたります。
そこで、オーバーライドされて1度見えなくなったメソッドを呼び出したい場合があります。
それにはキーワードsuperを使います。super.メソッド名
と書くことによって呼び出すことができます。
superを使わない場合
superを使う場合
実行結果はまったく同じですが、重複していた部分を削除することができました。
このように、継承関係においてスーパークラスの機能をそのまま利用したり、変更したり、補足したりさまざまな方法で扱うことができます。
継承とfinal
クラスは自分で作ったものだけではなく、他の人が作ったものでも継承できます。
逆に、自分が作ったクラスでも、いったん公開すれば誰がどこで使っているか分かりません。
勝手に継承して、いろいろな機能をオーバーライドしているかもしれません。
それが特に問題にならなければ良いのですが、場合によっては、勝手に継承やオーバーライドされたくない場合があります。
そういった場合は「final」キーワードを利用します。
finalキーワードはクラス、メソッド、フィールドに適用することができます。
ポイント!finalをクラスに付けた場合
finalをクラスに付けると、そのクラスはそれ以降、継承することができなくなります。
言い換えると、スーパークラスになることができなくなります。
public final class クラス名{ }
ポイント!finalをメソッドに付けた場合
finalをメソッドに付けると、そのメソッドはオーバーライドできなくります。
public class クラス名{ public final 戻り値 メソッド名(引数1,引数2… ){ } }
ポイント!finalをフィールドに付けた場合
finalをフィールドに付けると、そのフィールドは変更できなくなります。
言い換えると定数として扱われます。
public class クラス名{ public final 変数名 = 初期値; }
まとめ
メソッドのオーバーライド
- 継承したクラスのメソッドの機能を隠して、新しい機能を実装できます。
- オーバーライドされたメソッドは呼び出されなくなります。
final
- finalキーワードを使えば、クラスの継承やメソッドのオーバーライドをさせないことも可能です。
独学で挫折しそうになったら、オンラインプログラミングスクール