フィールドやメソッドをスーパークラスで共通で定義してサブクラス内で再利用することが出来ます。
例えば前のページで定義した APPLE サブクラスと ORANGE サブクラスの name フィールドと eat()メソッドの中身は全く同じなので、二度書くのは無駄な感じがします。
そこで次のように name と eat() を FRUIT スーパークラスに移して共通化してしまいましょう(ソース 1 )。
public class FRUIT { public String name; public void eat() { System.out.println(name+"美味い"); } }
次は APPLE と ORANGE サブクラスを書き換えますが、name フィールドと eat() メソッドはすでにスーパークラスで定義済みなので省略出来ます(ソース 2 と 3)。
ただし名前(name)は異なるのでコンストラクタで別々の名前を name フィールドにセットする必要があります。
なおスーパークラスで定義したフィールドにはドット演算子を使わなくてもサブクラスからそのままアクセス出来ます。
※ 正式には this.フィールド名 でアクセスします
public class APPLE extends FRUIT{ public APPLE() { name = "りんご"; // 正式には this.name = "りんご"; } }
public class ORANGE extends FRUIT{ public ORANGE() { name = "みかん"; // 正式には this.name = "みかん"; } }
main メソッドと実行結果は前のページと同じですが、一応再掲します。
public class Main{ public static void main(String[] args) { APPLE apple = new APPLE(); ORANGE orange = new ORANGE(); apple.eat(); orange.eat(); } }
りんご美味い みかん美味い
さてこの様にスーパークラスでフィールドやメソッドを共通化すると仕様変更に強くなるというメリットがあります。 例えば
仕様変更 「eat() メソッドの表示を"美味い"から"マズイ"に変えなさい」
という仕様変更は FRUIT スーパークラスをソース 4 のように書き換えるだけで済み、APPLE、ORANGE サブクラス、及び Main クラスは変更する必要ありません。
public class FRUIT { public String name; public void eat() { System.out.println(name+"マズイ"); } }
この時の実行結果は次の通りです。
りんごマズイ みかんマズイ
なおフィールドと同様、スーパークラスのメソッドはドット演算子を使わなくてもサブクラスから呼び出せます。
※ 正式には this.メソッド名 で呼び出します
例えば APPLE クラスを次のように書き換えたとしましょう。
この例では APPLE サブクラスの mogu_mogu() メソッドから FRUIT スーパークラスの eat() メソッドを呼び出しています。
public class APPLE extends FRUIT{ public APPLE() { name = "りんご"; // 正式には this.name = "りんご"; } public void mogu_mogu() { System.out.println( name+"もぐもぐ"); eat(); // FRUIT スーパークラスの eat() メソッドを呼び出す // 正式には this.eat(); } }
それでは、この様にして定義した APPLE.mogu_mogu() メソッドを以下の Main クラスから呼び出してみましょう。
public class Main{ public static void main(String[] args) { APPLE apple = new APPLE(); ORANGE orange = new ORANGE(); apple.mogu_mogu(); // eat の代わりに mogu_mogu を呼び出す orange.eat(); } }
結果は次の通りです。
りんごもぐもぐ りんごマズイ みかんマズイ
ところでスーパークラスにコンストラクタが定義されている場合はどのタイミングで呼び出されるのでしょうか。
例えばソース8の様に FRUIT を書き換えてみます。
public class FRUIT { public String name = "果物"; public FRUIT() { System.out.println( name + "大好き"); } public void eat() { System.out.println(name+"マズイ"); } }
この時の実行結果は以下の様になります。
果物大好き 果物大好き りんごもぐもぐ りんごマズイ みかんマズイ
この様に各サブクラスのコンストラクタが実行される前にスーパークラスである FRUIT の コンストラクタが自動的に実行されるため、「〜大好き」と表示される時点では name にはまだ "果物" が入っています。
その後各サブクラスのコンストラクタが実行されて name に "りんご" とか "みかん" がセットされ、後に eat メソッドが呼び出される時点ではそれらが表示されます。
スーパークラスのコンストラクタ呼び出しについてはこれ以上詳しくは触れませんが、とても使い道の多いテクニックですので興味のある人は「コンストラクタ super」などのキーワードで調べてみて下さい。