人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

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


●質問者: halyan
●カテゴリ:コンピュータ
✍キーワード:Java object オブジェクト キャスト 普通
○ 状態 :終了
└ 回答数 : 7/7件

▽最新の回答へ

1 ● quintia
●20ポイント

http://ask.jp/

Ask.jp

「オブジェクト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パターンなどを使うことを考えるべきです。

◎質問者からの返答

ありがとうございます。

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

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

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

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


2 ● s
●10ポイント

http://www.yahoo.co.jp/

Yahoo! JAPAN

URLはダミーです。

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


ClassA f(Object o){

return (ClassA)o;

}

◎質問者からの返答

ありがとうございます。

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


3 ● quintia
●20ポイント

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です

}


}

◎質問者からの返答

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

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


4 ● noragrammer
●20ポイント

http://dummy.co.jp/

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

通常、そういう用途には、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から見ると自然です。

◎質問者からの返答

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

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


5 ● noragrammer
●10ポイント

http://dummy.co.jp/2nd

別解は、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 ) {

// 処理コード

}

}

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

◎質問者からの返答

ありがとうございます。

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


1-5件表示/7件
4.前の5件|次5件6.
関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ