抽象クラスはそれ自体はインスタンスが作られることはありません。必ず継承先のサブクラスで全ての実装が行われ、クラスに抽象を表すabstractのキーワードのないクラスを利用してインスタンスを作ることになります。このことから実行時には抽象のままで利用されるクラスもメソッドも存在しないことになります。
抽象クラスでは抽象メソッドを利用した実装ができます。利用する抽象メソッドは実行時には実装されているものとみなして実装を行うことになります。
実装例
前回の実行クラスを見ましょう。
public class AnimalTest {
public static void main(String[] args){
Animal[] animals = new Animal[]{new Lion(), new Camel()};
for(int i = 0;i < animals.length;i++){
System.out.println("私は" + animals[i].getName() + "、" + animals[i].getKind() + "です。");
System.out.println("時速" + animals[i].getSpeed() + "kmで走ります。");
}
}
}
forのループの中で標準出力がされています。
System.out.println
を利用している2行を抽象クラスに実装し、実行クラスをすっきりさせます。まず現在の抽象クラスAnimalです。
public abstract class Animal {
public String getKind(){
return "動物";
}
abstract String getName();
abstract int getSpeed();
}
ここで宣言されている抽象メソッドgetName、getSpeedと実装されているメソッドgetKindを利用します。
public void print(){
System.out.println("私は" + getName() + "、" + getKind() + "です。");
System.out.println("時速" + getSpeed() + "kmで走ります。");
}
実行クラスからコピーしたもののにメソッドの宣言をつけたものです。実行クラスではインスタンスを指定してメソッドを読んでいましたがクラスの内部のメソッドを呼ぶためメソッドだけの指定でよくなります。
public abstract class Animal {
public String getKind(){
return "動物";
}
abstract String getName();
abstract int getSpeed();
public void print(){
System.out.println("私は" + getName() + "、" + getKind() + "です。");
System.out.println("時速" + getSpeed() + "kmで走ります。");
}
}
printメソッドで利用するメソッドが抽象メソッドだとしても戻り値は宣言されているのでその見込みで実装します。getNameメソッドはStringを返し、getSpeedメソッドはintを返すことはここで予想できるので実装に支障はありません。
実行クラスも以下のようにシンプルになります。
public class AnimalTest {
public static void main(String[] args){
Animal[] animals = new Animal[]{new Lion(), new Camel()};
for(int i = 0;i < animals.length;i++){
animals[i].print();
}
}
}
実行結果です。以前の出力結果は同じですので外からはわかりません。しかし処理の実装されているクラスは異なります。あるべきクラスに実装をしておくことはコードの再利用を促します。また追加で実装するときには労力の違いとなることもあります。
$ javac AnimalTest.java $ java AnimalTest 私はライオン、動物です。 時速70kmで走ります。 私はラクダ、動物です。 時速40kmで走ります。