- PR -

スーパクラス型の変数へサブクラス型を代入する意味

1
投稿者投稿内容
ピノコ
会議室デビュー日: 2003/09/10
投稿数: 7
投稿日時: 2003-09-10 16:10
初めて質問します。
JAVAは自分で勉強し始めてまだ1ヶ月ほどです。
基本的な質問をしても良い場なのか・・違ったら教えてください。
質問は

// 抽象クラス
abstract class Oya {
     private int price = 1980;
     int getPrice() {
          return price;
     }
     // 抽象メソッド
     abstract int sales();
}

// 実装クラス
class Ko extends Oya {
     int sales() {
          double d = getPrice()*0.9;
          return (int)d;
     }
}

class CTest {
     public static void main(String[] args) {
          Ko koObj = new Ko();   
     ******* Oya oyaObj = koObj;
          System.out.println("Price: " + oyaObj.getPrice());
          System.out.println("90%: " + oyaObj.sales());
     }
}

このようなプログラムで、*******の部分でスーパークラス型の変数oyaObjにサブクラスの
koObjを代入してます。こういったことが可能である、継承といったJAVAの規則は分かった
のですが

 Oya oyaObj = koObj; この命令消して

 System.out.println("Price: " + oyaObj.getPrice());
 System.out.println("90%: " + oyaObj.sales
 この2つの命令の oyaObj を koObj に変えても結果は同じになります。

どうしてわざわざ Oya の型に代入するのですか。
実際に何か作成したことがあるわけではないので、サブクラスの型のままで
進めればよいのではと思ってしまいます。

スーパークラス型にサブクラス型を代入しても、
スーパクラス型ではサブクラスで定義しているメソッドなどは使えないんですよね。

サブクラス型なら継承しているのだから、スーパークラスとサブクラスのメンバーを
指定できますよね。
代入して、スーパークラス型の変数(oyaObj)使うだけ、損な気がしてしまいます。
スーパークラス型にサブクラス型を代入するときはどんな処理をしたい時なのでしょうか。
uk
ぬし
会議室デビュー日: 2003/05/20
投稿数: 1155
お住まい・勤務地: 東京都
投稿日時: 2003-09-10 16:28
引用:

ピノコさんの書き込み (2003-09-10 16:10) より:
どうしてわざわざ Oya の型に代入するのですか。
実際に何か作成したことがあるわけではないので、サブクラスの型のままで
進めればよいのではと思ってしまいます。



支障がなければ親の型で宣言する必要はありません。ご質問に書かれている例であれば
親の型で宣言する意味はまったくないですね。

引用:

スーパークラス型にサブクラス型を代入しても、
スーパクラス型ではサブクラスで定義しているメソッドなどは使えないんですよね。

サブクラス型なら継承しているのだから、スーパークラスとサブクラスのメンバーを
指定できますよね。
代入して、スーパークラス型の変数(oyaObj)使うだけ、損な気がしてしまいます。



そもそもサブクラスで宣言したメソッドなりフィールドを使用したいのであれば当然
サブクラスの型で宣言すべきです。

引用:

スーパークラス型にサブクラス型を代入するときはどんな処理をしたい時なのでしょうか。



ではJavaの標準クラスでちょっと考えて見ます。

void foo(InputStream stream) {

}

void foo(ByteArrayInputStream stream) {

}

の二つがあった場合、後者にはByteArrayInputStreamまたはそのサブクラスのインスタンス
しか指定できません。しかしfooメソッド内で特にByteArrayInputStreamに特化した処理を
していないのであれば、前者で宣言したほうが使用できる範囲が広がりますね。

これだけだと理解しづらいかもしれませんが、オブジェクト指向の基本的な要素である
ポリモーフィズムとか、型と実装の分離にかかわる部分なので、そういった書籍を探して
みるといいかもしれません。
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-09-10 18:29
こんにちは、Wataです。
次のようなコードの場合はどうでしょう?
コード:
abstract class Super {
    public abstract void print();
}
class Sub1 extends Super {
    public void print(){
        System.out.println("Sub1だよ");
    }
}
class Sub2 extends Super {
    public void print(){
        System.out.println("Sub2です");
    }
}
public class Main {
    public static void main(String[] args){
         Super[] objects = {
             new Sub1(), new Sub2()
         };
         for(int i=0;i<objects.length;i++){
             objects[i].print();
         }
    }
}



ArrayList removeRangeの使い方スレッド内の
List list = new ArrayList();
とする意味についての記述も参考になると思いますよ。

ちなみに、私はサブクラスをスーパークラス型として扱いたい場面がないなら、
継承をすべきでないと思っています。
uk
ぬし
会議室デビュー日: 2003/05/20
投稿数: 1155
お住まい・勤務地: 東京都
投稿日時: 2003-09-10 18:50
引用:

Wataさんの書き込み (2003-09-10 18:29) より:
ちなみに、私はサブクラスをスーパークラス型として扱いたい場面がないなら、
継承をすべきでないと思っています。



これはどうかなあ。将来的にどう使われるかわかりませんしね。
ただしサブクラスのインスタンスをスーパクラスの型で扱うのが適当かどうかは
検証すべきでしょう(よくいうis-a関係かどうか、ということですね)。

継承というと実装継承の特徴である差分プログラミングに目が行ってしまいがちですが、
クラス継承であっても型も継承すること、そちらのほうが本質的であることを理解していない
とこの辺は難しいでしょうね。
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-09-10 19:07
引用:

ukさんの書き込み (2003-09-10 18:50) より:
引用:

Wataさんの書き込み (2003-09-10 18:29) より:
ちなみに、私はサブクラスをスーパークラス型として扱いたい場面がないなら、継承をすべきでないと思っています。


これはどうかなあ。将来的にどう使われるかわかりませんしね。


将来の話は今は考えない。XP的に。まあ、極力ぐらいにしておきましょう。
あと、骨格実装抽象クラスは例外ですね。AbstractList型を使う人はいないでしょう。

引用:

ただしサブクラスのインスタンスをスーパクラスの型で扱うのが適当かどうかは検証すべきでしょう(よくいうis-a関係かどうか、ということですね)。

継承というと実装継承の特徴である差分プログラミングに目が行ってしまいがちですが、クラス継承であっても型も継承すること、そちらのほうが本質的であることを理解していないとこの辺は難しいでしょうね。


ここは同意です。差分プログラミングの多くは、機能の再利用が目的でだったりしますが、
そういう場面の多くはis-a関係を満たしません。
(例えば、java.util.Propertiesクラスとか)
多少の手間を惜しまずにコンポジションを使うべきなのです。
ピノコ
会議室デビュー日: 2003/09/10
投稿数: 7
投稿日時: 2003-09-11 10:16
お返事ありごとうございます。

ukさんへ。
支障がなかったり、無理に親型で宣言する必要があるわけではないことがわかりました。
ありがとうございます。
ByteArrayInputStream、ポリモーフィズム、型と実装の分離のあたりは
これからもっと理解を深めたいと思います。

Wataさんへ。
記述していただいたコードはとてもよくわかりました。
どんな時に使うんだろう・・という謎が少し解明されました。
「ArrayList removeRangeの使い方スレッド内の
List list = new ArrayList();
とする意味についての記述」
勉強の参考にします。
ありがとうございます。

1

スキルアップ/キャリアアップ(JOB@IT)