インスタンス生成時に指定したインスタンスの型によるふるまいの違い

AbstractSample s = new ConcreteSample();
ConcreteSample c = new ConcreteSample();

どちらも実体は具象クラスですので、実体の出来ることは同じです。
違うのは変数の型であって、実体ではありません。

変数の型の違いによって出来ることの何が違うのかと言えば、 AbstractSample 型である s (を通して実体である ConcreteSample 型オブジェクトを使う時) は AbstractSample 型に書かれていることしか出来ませんし、 ConcreteSample 型である c (を通して(ry ) は ConcreteSample 型の全ての機能が使えます。

変数は実体への窓口みたいなもので、何でも出来る医者に対し、内科として会えば内科の医師として見え、外科として会えば外科の医師として見える・・・みたいなことです。

質問のコードは疑問に思われている違いを見るようなコードではありませんので、ちょっと変えてみましょう。

java

1abstract class AbstractSample {2 public void sample() {3 System.out.println("A");4 }5}6 7class ConcreteSample extends AbstractSample {8 public void sample() {9 System.out.println("B");10 }11 public void test() {12 System.out.println("C");13 }14}15 16class Main {17 public static void main(String[] args) {18 ConcreteSample c = new ConcreteSample();19 AbstractSample s = c; //s も c も同じオブジェクト20 s.sample();21 //s.test(); //AbstractSample 型に test メソッドは無いのでコンパイルエラー22 c.sample();23 c.test(); //ConcreteSample 型なら test メソッドは有る24 }25}

1 つのオブジェクトを型の違う 2 つの変数で指していますが、抽象クラスの変数では具象クラスで増えたメソッドは使えません。

具象クラスのコンストラクターを用いているのに抽象クラスの型を用いてインスタンスを作成するのか

文字通りで少し意地悪な言い方をしますと、「abstract なクラスは直接インスタンスを作れないから」です。
・・・まぁお聞きしているのはそういうのでは無いでしょうね。

具象クラス(ConcreteSample) を new しているのに受け取る変数を抽象クラス(AbstractSample) やインターフェースにするといったことは、 java では List や Map/Set 等で良く見かけます。

List list = new ArrayList(); Map map = new HashMap(); Set set = new HashSet();

これは、 "何か" では無く "何が出来るか" という点から見るためです。
例えば List は、一連のデータを管理し追加や削除等をする為に使用します。 list 変数を List 型として使うだけなら実体が ArrayList かどうかはどうでも良いことです。
得てして何事も機能が多いことが良いように思われがちですが、良いことばかりではありません。プログラムの機能が増えればコードが増え、メモリを食い、間違えが増え、保守が面倒になり・・・要するにしたいことが出来る最小限のもので必要十分を得るというほうが、後々もラクなのです。

プログラムは、書いて動いたらお終いではありません。時には保守更新をしながら何年何十年もインフラとして動き続けるようなこともあります。
当然最後まで作った本人がメンテするとは限らず、今書いているコードは後に他の人が見て修正することになるかもしれません。
変数の型は、その変数が "何をする為にあるのか" を示す重要な情報になります。
List 型の変数なら List 型の機能だけを使っているはずですので他の具象クラスに変えても動作するはずですが、 ArrayList 型の変数は ArrayList 型固有の機能を使用している可能性が出てきます。(これは例えですので ArrayList 型固有の機能があると言ってるわけではありません。)
それを確認するにはその変数を使っている全ての個所を探して調べてみなければなりません。
だったら作った時にコメントでも書いておけば・・・

ArrayList list = new ArrayList(); //List 型の機能しか使いません

・・・本当に list が ArrayList の固有機能を使っていないか誰も保証できませんし、プログラムを書いた時はそうでも後に誰かがどこかで固有機能を使うように修正し、このコメントを修正していない可能性もあります。
だったら最初から

List list = new ArrayList();

としてしまえば、ArrayList 固有の機能を使おうとするとコンパイルエラーになりますし、コメントが無ければ修正忘れもありません。

これらのことを考慮して変数の型を決めると良いと思います。

コメントを投稿

0 コメント