- PR -

スーパークラスのキャストについて

投稿者投稿内容
fly_moon
会議室デビュー日: 2005/03/29
投稿数: 11
投稿日時: 2005-04-08 11:33
またもや初歩的な質問ですいません。
ダウンキャストについてです。
実際よく考えてから使うべきなのは解ったのですが、ではどうしても使いたいとき、どう使えばいいのでしょうか?

使い方がよくわからなくて、ClassCastExceptionが発生します。

/**
* 実行クラス
*/
class CastTest {
  public static void main(String[] args) {
    //スーパークラスのオブジェクトを作成
    CastObject obj=new CastObject();

    //サブクラスにダウンキャスト
    subObject1 s=(subObject1) obj;/*!![ClassCastException発生]!!*/

    s.setEx("cast");
    System.out.println(s.getEx());
  }
}

/**
* スーパークラス
*/
class CastObject {
}

/**
* サブクラス1
*/
class subObject1 extends CastObject{
  private String ex;

  //Getterメソッド
  public String getEx(){
    return ex;
  }

  //Setterメソッド
  public void setEx(String str){
    ex=str;
  }
}

インスタンスの違いから発生していると思うのですが、どう直せばいいのか考えても思いつきません。実行クラスの

CastObject obj=new CastObject();
 ↓
CastObject obj=(CastObject) Class.forName("subObject1").newInstance();

とすると例外は発生しなくなるんですが、他に方法は無いのでしょうか?

オブジェクト指向がしっかりと理解できていないと思うので、これがそのヒントになるような気がしています。ホントにわがままな質問ですが、どなたか教えてください。
宜しくお願いします。
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2005-04-08 12:05
たとえば、こういう風に使います。
コード:
  CastObject obj =new subObject1();
  subObject1 downed = (subObject1)obj;


ダウンキャストする場合は、あくまで、目的のクラスかまたは、
そのスーパークラスでないといけないわけです。

たとえば、「会社」をスーパークラス、「ト○タ」「ス○キ」をサブクラスとします。
この場合、「ト○タ」「ス○キ」はいつでも「会社」として扱えます。
逆に「会社」だからといって、「ト○タ」「ス○キ」そのものとして扱える訳じゃありません。

この辺はオブジェクト指向の「継承」というやつです。
ロス
常連さん
会議室デビュー日: 2005/03/25
投稿数: 26
投稿日時: 2005-04-08 12:33
例題のMainとは異なるのですが、、
1.サブクラスとしてインスタンスを生成してスーパークラス(またはインターフェイス)で扱っている。
2.なんらかの必要性があってサブクラスを識別する。

このような条件の場合、以下のような方法があります。
コード:

CastObject obj = hogeFactory.getInstance();
↑ SubObject1, SubObject2, SubObject3のどれかがインスタンス化されると仮定。

if (obj instanceof SubObject1) {
subObject1 downed = (subObject1) obj;
}
if (obj instanceof SubObject2) {
subObject2 downed = (subObject2) obj;
}
if (obj instanceof SubObject3) {
subObject3 downed = (subObject3) obj;
}




[ メッセージ編集済み 編集者: ロス 編集日時 2005-04-08 12:34 ]
(株)ぽち
ぬし
会議室デビュー日: 2002/09/10
投稿数: 376
投稿日時: 2005-04-08 13:45
引用:

ロスさんの書き込み (2005-04-08 12:33) より:
コード:
  CastObject obj = hogeFactory.getInstance();
  ↑ SubObject1, SubObject2, SubObject3のどれかがインスタンス化されると仮定。

  if (obj instanceof SubObject1) {
      subObject1 downed = (subObject1) obj;
  }
  if (obj instanceof SubObject2) {
      subObject2 downed = (subObject2) obj;
  }
  if (obj instanceof SubObject3) {
      subObject3 downed = (subObject3) obj;
  }





こういうケースってどういう時に有効なのでしょうか。

ファクトリから取得すべきオブジェクトが増えた場合、取得元で
コードの修正(instanceofの追加)が必須になりますよね。

キャストせずに親クラスのメソッド叩いて、実は中身の
実装が変更されてても叩き元は気にしない、という使用
方法なら理解できるのですが。。
fly_moon
会議室デビュー日: 2005/03/29
投稿数: 11
投稿日時: 2005-04-08 15:28
みなさんありがとうございます。

Edossonさん
引用:

たとえば、「会社」をスーパークラス、「ト○タ」「ス○キ」をサブクラスとします。
この場合、「ト○タ」「ス○キ」はいつでも「会社」として扱えます。
逆に「会社」だからといって、「ト○タ」「ス○キ」そのものとして扱える訳じゃありません。


なるほど。やっぱり、親クラスのオブジェクトを作成時に、子クラスのオブジェクトとして、作成して、「会社」=「ト○タ」としてから「会社」を扱うしかないのですね。
よくわかりました。


ロスさん
instanceofで条件分岐する時の、子クラスのリストを取得する方法があれば使用可能ですね。あと、似ている感じのコードについて書かれているページを見つけたのですが、[←ここ]の部分がどういう状況なのかよくわかりません。質問は変わってしまいますが、よろしければ、教えて頂けないでしょうか?(ホントは[←ここ]以外もよく解らないんですが)

http://www-6.ibm.com/jp/developerworks/java/010907/j_j-diag4.html#6

コード:
abstract class Tree {
}

class Leaf extends Tree {
  public static final Leaf ONLY = new Leaf();  //←ここ
}


自分の中に自分を持つ???ホントに物わかりが悪くてすいません。


ぼちさん
この間もお返事頂いてありがとうございました。

引用:

キャストせずに親クラスのメソッド叩いて、実は中身の
実装が変更されてても叩き元は気にしない、という使用
方法なら理解できるのですが。。



?????頭が悪くてすいません。どういう状況かわかりません。教えてください。
(株)ぽち
ぬし
会議室デビュー日: 2002/09/10
投稿数: 376
投稿日時: 2005-04-08 15:48
私もぺーぺーなもんで、interfaceのテクニカルな使い方は
よくわかっていませんが、interface使って何がうれしいって
実装を使用者側が意識しなくていいことだと思いまして。

コード:

public interface A {
public void execute();
}

public class B implements A {
public void execute() {
// PostgreSQL用コード
}
}

public class C implements A {
public void execute() {
// Oracle用コード
}
}

public class Main {
A a = HogeFactory.getInstance().getDAO();

// 使用者はDBがpostgresだろうがoracleだろうが気にしない
a.execute();
}


HogeFactoryはgetDAOで
return new B()もしくはreturn new C()で返すと。

DBにMySQL対応が入った場合は、class Dでも作ればいいです。
でもMain側にコードの修正はいらないです。

こういう使い方が定石?なのかな、と。

[ メッセージ編集済み 編集者: (株)ぽち 編集日時 2005-04-08 15:50 ]
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2005-04-08 15:55
引用:

なるほど。やっぱり、親クラスのオブジェクトを作成時に、子クラスのオブジェクトとして、作成して、「会社」=「ト○タ」としてから「会社」を扱うしかないのですね。
よくわかりました。


newで作成されているのは、あくまでも子クラスです。
「しかない」のではなく、これが当たり前なのですが、ま、ぼちぼちなれていきましょう。
引用:

自分の中に自分を持つ


「インスタンス」「生成」をキーワードに調べてみてください。
引用:

instanceofで条件分岐する時の


ふつうは、このような条件分岐は行いません。
こんな感じでしょうか。
コード:

class 会社{
abstruct public Product getNewestProduct();
}

class ト○タ extends 会社 {
public Product getNewestProduct(){
return ...;
}
}

class ス○キ extends 会社 {
public Product getNewestProduct(){
return ...;
}
}

class Test {
public static void main(String[] args) {
  会社[] companies = new 会社[10];
companies[0] = new ス○キ();
companies[1] = new ト○タ();
.....

for (int i = 0; i < companies.length; i++) {
Product product = companies[i].get();
System.out.println( product.toString() );
}
}
}



[ メッセージ編集済み 編集者: Edosson 編集日時 2005-04-08 16:00 ]
ロス
常連さん
会議室デビュー日: 2005/03/25
投稿数: 26
投稿日時: 2005-04-08 17:41
> (株)ぽち さん
引用:

こういうケースってどういう時に有効なのでしょうか。


「どうしても」サブクラスにしか定義されていないメンバやメソッドにアクセスする場合ですね^^;
設計上、そういう状態を避けるのが一番よいと思います。

> fly_moon さん
引用:

キャストせずに親クラスのメソッド叩いて、実は中身の
実装が変更されてても叩き元は気にしない、という使用
方法なら理解できるのですが。。


後の例でも出てくるのですが、「キャストせずに利用」=「親クラスのインターフェイスだけを意識して、実装は実際のインスタンス(子クラス)に委譲する」ということだと思います。

お二方の例を見ると分かるように、ダウンキャストが必要になると思ったら、本当に必要なのかどうかを設計レベルで見直したほうがよいと思います。
((株)ぽちさんは「インターフェイス」を使って実装クラスに動作を任せています)
(Edossonさんは「アブストラクトクラス」を使って子クラスに動作を委譲しています)

最初の例で言うと、(親クラスがアブストラクトクラスだとして)
/**
* 実行クラス
*/
class CastTest {
  public static void main(String[] args) {
    //スーパークラスのオブジェクトを作成
    // CastObject obj=new CastObject();
    CastObject obj=new subObject1();

    //サブクラスにダウンキャスト
    // subObject1 s=(subObject1) obj;/*!![ClassCastException発生]!!*/

    // s.setEx("cast");
    obj.setEx("cast");
    // System.out.println(s.getEx());
    System.out.println(obj.getEx());
  }
}

/**
* スーパークラス
*/
class abstruct CastObject {
> //Getterメソッド
> public abstruct String getEx();
>
> //Setterメソッド
> public abstruct void setEx(String str);
}

/**
* サブクラス1
*/
class subObject1 extends CastObject{
  private String ex;

  //Getterメソッド(オーバーライド)
  public String getEx(){
    return ex;
  }

  //Setterメソッド(オーバーライド)
  public void setEx(String str){
    ex=str;
  }
}

としてみてはどうでしょうか?

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