先日、abstractクラスを継承したオブジェクトでメソッドを呼び出す説明をするときにこの例えありかもな、と思ったので備忘として。
実装の例
public abstract class Animal{
public void bark(){
System.out.println("An animal barks.");
}
public void sleep(){
System.out.println("An animal sleeps.");
}
public void eat(){
System.out.println("An animal eats.");
}
}
public class Dog extends Animal{
public void sleep(){
System.out.println("A dog sleeps.");
}
}
public class Shibainu extends Dog{
public void bark(){
System.out.println("The shibainu barks.");
}
}
public class Main{
public static void main(String...args){
Shibainu shibainu = new Shibainu();
shibainu.bark();
shibainu.sleep();
shibainu.eat();
}
}
これの実行結果を考えてみましょう。
貫通するイメージとは
各クラスでメソッドが定義されている=貫通が止まる。なければ貫通するイメージ。
※「貫通」は筆者のイメージ上の単語なので、一般的な用語ではありません。
※貫通をきちんとした言い回しに変えるなら、「メソッド呼び出しの際、継承されたクラスの階層を順に上にたどる過程」といえます。
Shibainuのオブジェクトを作った場合
Shibainu ここから始まって、呼び出しているメソッドを探す。
あれば貫通が止まる。なかったら貫通する。
↓
↓ 貫通
↓
Dog Shibainuではなかったメソッドを探す。
あれば貫通が止まる。なかったら貫通する。
↓
↓ 貫通
↓
Animal Shibainu、Dogではなかったメソッドを探す。ここまで行き着いたのであれば
必ずメソッドが存在するはず。なければエラーになる。
①shibainu.bark();について
shibainu.bark():
最初に見るのはShibainuクラス。
Shibainuクラスにbarkが定義されているので、この時点で貫通は止まる。
//出力結果
The shibainu barks.
②shibainu.sleep();について
shibainu.sleep():
最初に見るのはShibainuクラス。
Shibainuクラスにsleepは定義されていないので、貫通する。
次に見るのはDogクラス。
Dogクラスにsleepが定義されているので、この時点で貫通は止まる。
//出力結果
A dog sleeps.
③shibainu.eat();について
shibainu.eat():
最初に見るのはShibainuクラス。
Shibainuクラスにeatは定義されていないので、貫通する。
次に見るのはDogクラス。
Dogクラスにeatは定義されていないので、貫通する。
最後に行き着くのがAnimalクラス。
Animalにeatは定義されているため、この時点で貫通は止まる。
//出力結果
An animal eats.