Javaで例えばある関数をsave(ClassA o)で呼ばれる時,渡すオブジェクトaがObject型であるとき渡すにはsave((ClassA) a)とするのが普通ですが。明示的にCalssAでキャストせず例えばsave( f(a) )のようにfという関数で動的にキャストする方法はあるのでしょうか。説明が明確でないですがお知恵をお願いします。

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:2005/07/30 14:24:35
  • 終了:--

回答(7件)

id:quintia No.1

quintia回答回数562ベストアンサー獲得回数712005/07/30 15:17:57

ポイント20pt

「オブジェクトaがObject型である」時には「save((ClassA) a)」としてもsaveメソッドは呼び出せません。

aがClassAから派生したクラスのインスタンスでは無いからです。


好意的に解釈して、「変数aがObject型のであるとき渡すにはsave((ClassA) a)とするのが普通です」の書き間違いだとしましょう。


1.その場合にsave(ClassA o)に引数として渡すことができるのはa変数に代入されているオブジェクトが、ClassAのインスタンスか、またはClassAのサブクラスのインスタンスの場合のみです。

この場合、どちらであっても、save((ClassA) a)として呼び出して構いません。


2.a変数に代入されているオブジェクトが、ClassA(とそのサブクラス)以外のインスタンスであったなら、saveメソッドの引数にすることはできません。無理にキャストしようとすれば、ClassCastExceptionが生じます。


1.を擬似コードで説明します。

class ClassB extends ClassA{

:

}

と宣言したClassBがあったとして、

ClassA a = new ClassB();

という「より上位のクラスの変数に対しての暗黙のアップキャスト」が有効であることを認識してください。


Object a = new ClassB();

同様にこれも上位のクラスの変数に対しての暗黙のアップキャストですので有効です。


save((ClassA)a);

aがObject型であるため、ClassAにダウンスキャストすることを明示してはじめてコンパイル可能です。

実行も問題ありません。


2.の方は

class ClassC extends Objett {

:

}

と宣言したClassCがあったとして、

ClassA a = new ClassC();

は不当です。コンパイルが通りません。


Object a = new ClassB();

は問題ありません。


しかし、

save((ClassA)a);

は実行できません。実行時にClassCastExceptionが生じます。


何らかの理由でClassAと、ClassAを派生しないClassCとの間で共通的な処理を、同じsaveメソッドで行いたい、という必要が生じているのであれば、interfaceを利用する必要があるでしょう。


interface CommonMethodBetweenAandC {

:

}


class ClassA implements CommonMethodBetweenAandC {

}


class ClassC implements CommonMethodBetweenAandC {

}


class UtilitiOfAandC {

static void save(CommonMethodBetweenAandC);

}


という感じの宣言になり、


ClassA a = new ClassA();

ClassC c = new ClassC();


UtilitiOfAandC.save(a);

UtilitiOfAandC.save(c);


という様な使い方になります。


あるいは、http://www.hatena.ne.jp/1121941919 で2.の回答にあった通り、Proxyパターンなどを使うことを考えるべきです。

id:halyan

ありがとうございます。

save(ClassA a)はsave(ClassA a) { ... }

のことでObject o がObjectクラスの時

class CalssA extend Object { ... }です。

ある関数からsave関数を呼びだしたときsave((ClassA) o)ですが明示的にClassAでキャストするのではなくCassAを変数のように変えたいのです。そんな方法をさがしています。

2005/07/30 17:50:27
id:sshi No.2

s回答回数14ベストアンサー獲得回数12005/07/30 17:47:12

ポイント10pt

URLはダミーです。

「動的に」というのがちょっと意味とれてないですが、ぱっと思いつくのは、関数fの中でキャストする方法でしょうか。こんなかんじかな


ClassA f(Object o){

return (ClassA)o;

}

id:halyan

ありがとうございます。

明示的に(ClassA)ではなくキャスト自身を変更したい。つまりoをCassAでもClassBでも自由に変更したい。もちろんClassAもClassBもObjectクラスを継承しています。

2005/07/30 19:06:28
id:quintia No.3

quintia回答回数562ベストアンサー獲得回数712005/07/30 19:14:03

ポイント20pt

http://www.gremlin.jp/yada/j_java_int_r.htm

インターフェースと多態性

> ClassAを変数のように変えたいのです。そんな方法をさがしています。

全く見当違いです。

http://www.hatena.ne.jp/1121941919 で2.の回答者の答えが、Java的に正しい方向です。

あの2.の回答の意味について真剣に検討してください。

回答の意味が判らないのであれば、勉強しなければなりません。

貴方は、完全に、勘違いをして、間違った道を、模索していると、その様に認識してください。

このままでは誰も回答できなくなってしまいます。


> 明示的に(ClassA)ではなくキャスト自身を変更したい。

> つまりoをCassAでもClassBでも自由に変更したい。

> もちろんClassAもClassBもObjectクラスを継承しています。

具体的にsave(ClassA o) メソッドの中で、渡したインスタンスoのどんなメソッド(やメンバ変数)を呼んでいるのかを考えてください。


1.

Objectクラスに属するメソッドしか呼び出していないのですか?

であれば、save(ClassA o)である必要は全くありません。save(Object o)という宣言でいいですね?


void save(Object o) {

:

}


void hoge() {

ClassA a = new ClassA();

save(a);

}

が許されることは判りますね?

これが判らないのであれば、末尾のプログラムを動かしてみてじっくり考えてください。


2.

save(ClassA o) メソッドの中で、ClassA特有のメソッドを呼んでいますか?

void save(ClassA o) {

:

o.foo(); //foo はClassAに固有のメソッド

:

}

ということです。


2.1.

さて、このようなメソッドに対してClassBを(キャストしてでもなんとしても)渡したいというのが、ナンセンスだということに気が付きませんか?

上のsaveメソッドのo.foo();が失敗しますね? ClassBが、ClassAのメソッドを持っているはずは無いのですから!


2.2.

それとも、ClassBにもfoo();メソッドがあるというのですか?

そして、ClassBを(キャストしてでもなんとしても)渡してやれば、そのfooメソッドを読んでくれることを期待しているのですか?

それは間違いです。

あなたは「ClassAもClassBもObjectクラスを継承しています。」と言っています。

ClassAとClassBにたまたま同じ名前のメソッドがあったとしても、それらには――つまり、ClassAのfoo()メソッドとClassBのfoo()メソッドには――何の関わりもありません!


この場合はどうすればいいのか? 回答はいくつかありますが、1.で示したinterfaceを使うやり方がそのうちの1つです。


別の例を示しましょう。


abstract class FoundatationClass {

public abstract void foo();

}


class ClassA extends FoundatationClass {

public void foo() {

:

}

}


class ClassB extends FoundatationClass {

public void foo() {

:

}

}


に継承関係を変更し、saveメソッドは


void save(FoundatationClass o) {

:

o.foo(); //foo はFoundatationClassを継承した具象クラスに備わっているはずのメソッド

:

}


とするのです。


3.

saveメソッドの中身はまだ判らないが、ObjectAが来てもClassBが来ても動くようにしたいということでしょうか?

ナンセンスです。

それではsaveメソッドの中で、ClassAかClassBかを判断してif文を羅列するなどという処理を必要にするだけです。

何の意味もありません。

saveメソッドを引数ごとに用意した方がまだマシです。


2.2.のケースで実際に動くプログラムを示します。

実行してよく考えてみてください。


public class Untitled1 {

static class ClassA extends Object {

public String getData() {

return ”ClassAです”;

}

}


static class ClassA1 extends ClassA {

public String getData() {

return ”ClassA1です”;

}

}


static class ClassA2 extends ClassA {

public String getData() {

return ”ClassA2です”;

}

}


static class ClassA3 extends ClassA {

public String getData() {

return ”ClassA3です”;

}

}


static class ClassA4 extends ClassA {

public String getData() {

return ”ClassA4です”;

}

}


public void save(ClassA a) {

//saveという名前のクラスだが、ここでは標準出力に書き出すだけ。

System.out.println(a.getData());

}


public static void main(String[] args) {

Untitled1 self = new Untitled1();

ClassA o = new ClassA2();

self.save(o);

//↑ここで何が出力されるでしょうか?

//saveメソッドの引数の宣言がClassAだからClassAのメソッドが呼び出されるでしょうか?

//それともClassA2のメソッドでしょうか?


o = new ClassA3();

self.save(o);

//↑ここでは?

o = new ClassA4();

self.save(o);

//↑ここでは?


//↓のように出力されます

//ClassA2です

//ClassA3です

//ClassA4です

}


}

id:halyan

丁寧な返答ありがとうございます。

実はsave関数はO/Rマッピングフレームワークのheibernateの関数です。内部構造は不明ですがおそらくクラスを解析して対応するクラスにマッピングしていると思いますが、このsave関数は引数にマッピングするクラスのオブジェクトを渡さなければいけなくテーブルが20個あった場合それに対応するクラスを20個作成するのですがデータアクセス用のDAOも20個必要になるため(つまりsave(table1Calss a),...,save(table20Class a)のように)それを一つのDAOで行いたいのです。そのためaオブジェクトをそれぞれのクラスに動的にキャストする方法を模索しています。

2005/07/30 19:46:45
id:noragrammer No.4

noragrammer回答回数4ベストアンサー獲得回数02005/07/30 19:35:00

ポイント20pt

うーん、言いたいことは無理そうだなぁ。

通常、そういう用途には、Interfaceを使います。

大体、変数型としてObjectなだけで実行時は、実行型として、Class Aにbindされているはずですから、型変換関数を使う必要がないのです。

動的キャストの意味自体がやや意味不明ということですね。

どうしてもそういう関数を書きたいのであれば、

A f(Object o ){

if ( o instanceOf(A) ){

return (A) o;

} else if ( o instanceOf(B) {

//class conversion prosess

  B b = (B)o;

A a = new A();

a.x = b.x;

a.y = b.y ;

return a ;

}

}

のように、instanceOfとかを使えば書けないこともないでしょうけれど。やんないほうが普通な気がするなぁ。

多分、やろうとしていること自体がInterfaceをうまく使うことで切り抜けれる性質の問題だと思います。

とりあえず、A.save();パラメータ無しのメソッドを使うほうがInterfaceから見ると自然です。

id:halyan

ありがとうございます。たぶんsave関数内部ではそのような動作をしていると思いますが、このsave関数は

実はO/RマッピングのHibernateなのです。そのためマッピングするテーブルの数だけsave関数が必要なのです。それは引数が各テーブル対応のクラスのオブジェクトを渡さなければいけないからです。save関数に渡すオブジェクトをそれなのクラスに動的にキャストするればsave関数一つで処理できると考えたのですが無理でしょうか。

2005/07/30 19:53:05
id:noragrammer No.5

noragrammer回答回数4ベストアンサー獲得回数02005/07/30 19:45:36

ポイント10pt

別解は、instanceOfを使って、save関数で処理を切り替えることです。こちらはある意味自然かな。でもクラス数が増えると管理が大変になるから、やっぱりInterfaceがお勧めではあります。


class C {

void save(Object o ) {

if ( o instanceOf(A) ) {

return save((A)o);

} else if ( o instanceOf(B) ){

return save((B)o);

} else {

// その他の処理

}

}

void save(A a ) {

// 処理コード

}

void save(B b ) {

// 処理コード

}

}

これだと、呼ぶほうはとりあえず気にせずに呼べば、クラス対応のルーチンに行くはずです。

id:halyan

ありがとうございます。

やはりこういう方法になるのでしょうか。

2005/07/31 11:09:32
id:renmin-plus No.6

renmin-plus回答回数23ベストアンサー獲得回数02005/07/31 14:55:58

ポイント10pt

ちょっとずれてしまうかもしれませんが、


Hibernate という前提の話なんですが、Session#save(Object o) は Object 型を引数にとり、それがどのテーブルにマップしているかは勝手に判別してくれるわけですから、その気になればメソッドひとつで簡単に済むと思うのですが、ぼく何か勘違いしていますか?


あと、DAOを20個作るというのは、やり方としては正当だと思います。


最後に、これは直接質問の回答になっているかもしれませんが、Hibernate の Session#save(Object) がどうやって型を変換しているのかというと、これは、SessionFactory の構築時にマッピング用クラスの Class オブジェクトと、それぞれの永続化のために用いる情報を Map に格納して、それを利用して変換しています。一つ、永続化用クラスをまとめて記述した設定ファイルを作成して同じような手法を用いれば、質問の内容を実現できるかと思います。それだとキャストとは言えませんし、前半に書いたとおり、それが必要というのがよくわかりません。

id:halyan

ありがとうございます。

実はsave関数に関しては別にsava(String entity, Object obj)形式の関数があり解決しました。つまりentityでマッピング対応のクラス名を渡せることが判りました。ただ少し話が変わるのですがsave関数に渡すobjectにデータをセットするにはテーブルにマップしているクラス型のインスタンスを利用しなければいけないので動的にインスタンスを作成する必要があります。もちろんこれ自体はnew でできるのですが例えばクラス名の文字列からインスタンスを作成出来ない物か思案しています。つまりコードとして記述するのでなく、クラス名を記述したファイルを読み込んでそれを元にそのインスタンスを作成できる方法を考えていて

キャストを動的にそのクラス名でキャストできれば解決するのかと考えていました。DAO20個の方法は考えていました。ん・・難しいものですね

2005/07/31 18:36:17
id:renmin-plus No.7

renmin-plus回答回数23ベストアンサー獲得回数02005/07/31 19:12:18

ポイント10pt

save(String, Object) は dynamic model と言って(URL参照)、Object には Map を主に使います。だから、もしそれを使うというなら、マッピングインスタンスの動的生成は不要です。逆にマッピングファイルでテーブルと関連付けているクラスであれば、save(Object) で勝手にその関連を見つけてくれるから、エンティティ名指定は不要です。だからやっぱり不要かなと思うんですが、一応、クラス名からインスタンスを動的生成したければ、


Class.forName(”class.Name”).newInstance();


でできます。

id:halyan

ありがとうございます。

save関数は引数にマッピングクラスのインスタンスを渡さなければ出来ません。Object型ではどのクラスから判らないからです。確認しています。

また、newInstance()は戻り値がObjet型のため明示的にキャストしなければ利用できません。

2005/08/02 20:09:24
  • id:quintia
    問題はインスタンス生成でなくて生成したインスタンスにどう処理するか? なのでは

    http://www.hatena.ne.jp/1122802480
    と合わせて読んでみても何がしたいのか全然伝わってこない。

    TableAとTableBがある。
    カラムはIDのみ。
    TableAクラスとTableBクラスがあって、カラムに対応するget/set用のメソッドは書いてある。
    IDカラムが1〜100までのレコードを作る。
    などという無理矢理なシチュエーションを考えよう。

    import java.lang.reflect.*;


    (略)
    Configuration config = new Configuration();
    config = config.configure();
    SessionFactory sessionfactory = config.buildSessionFactory();
    Session session = sessionfactory.openSession();

    Transaction trans = session.beginTransaction();

    String[] tables = { ”TableA”, ”TableB” };

    for (int n = 0, h = tables.length; n < h; n++){
     Class c = Class.forName(tables[n]);
     Object o = c.newInstance();

     //※1
     Class[] paramType = new Class[] {Integer.class};
     Method m = c.getMethod(”setID”, paramType);
     Object[] param = new Object[1];

     //※2
     for (int i = 1; i <= 100; i++) {
      //※3
      param[0] = new Integer(i);
      m.invoke(o, param);
      

      session.save(o);
     }

    }

    例外処理とかTransactionは書いてないけど,これで十分だよなぁ。
    今、IDカラムしかないテーブルに1〜100までInsertする、などという無理矢理なシチュエーションなのでこれで済んでいるけど、実際にやりたいことはそんなのでは済まないはずだ。
    何がやりたいかによって※1〜※3にどういう風に書くのか? という問題は出るけど、インスタンスの生成云々の話は全く問題ないと思うのだけど。


    それとも、TableAクラスとTableBクラスがあって、それぞれで同じことをさせたいとかなら、

    interface SameOperationAandB {
     public void foo();
    }
    class TableA implements SameOperationAandB {
     public void foo(){
     :
     }
    }
    class TableB implements SameOperationAandB {
     public void foo(){
     :
     }
    }

    と宣言して、

    String[] tables = { ”TableA”, ”TableB” };

    for (int n = 0, h = tables.length; n < h; n++){
     List list = session.find(” FROM ” + tables[n] ” AS TBL WHERE TBL.flg = 1); //定型の検索処理の実行
     for(int i=0;i < list.size();i++){
      SameOperationAandB o = (SameOperationAandB)list.get(i);
      
      o.foo();
      
      session.save(o);
     }
    }
    とする。

    Hibernateは今初めて見てみているけど、こんなんでいいと思う。

この質問への反応(ブックマークコメント)

トラックバック

  • quinta essentia quinta essentia 2006-03-13 16:13:05
  • うーん、二回以上回答できないらすぃw うーん、二回以上回答できないらすぃw 2006-03-13 16:13:05
    http://www.hatena.ne.jp/1122701075 ということ...
  • [hatena]やっと [hatena]やっと 2006-03-13 16:13:05
    実はsave関数はO/Rマッピングフレームワークのheibernateの関数です。内部構造は不明ですがおそらくクラスを解析して対応するクラスにマッピングしていると思いますが、このsave関数は引数に
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません