- PR -

Widening

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-05-06 12:00
unibon です。こんにちわ。

引用:

ジョーカーさんの書き込み (2003-04-21 23:31) より:
Serializeが関係ありそうな気がしますが、ファイルに一旦書き出し
たりしたくないです。


これについては、
java.io.ByteArrayOutputStream
java.io.ByteArrayInputStream
などを使うとメモリ上でストリームを扱えます。
別の掲示板ですが、これらのクラスを使った例を書いたことがあります。
http://www.vc-net.ne.jp/~ytp/bbs/java/bbs9205.html

ただし、シリアライズの機能を使ったとしても、
class A のインスタンスを class B のインスタンスとして
コピーすることはできないように思います。
Object クラスの clone メソッドもやはり、
できないようです(class A のインスタンスは
class A のインスタンスとしてのみコピーできる)。

引用:

ジョーカーさんの書き込み (2003-05-04 14:46) より:
HIR様
結論としては、この方法を採用しました(リフレクションを使って)。
しかし、本当はクラスAにcopy(cast)メソッドを追加したくない。
この操作はクラスAの機能ではなく、ユーティリティ的な扱いとしたい。


私も copy メソッドを自前で作るのが良いと思います。
ちなみにこのメソッドは class A に作っても class B に作ってもいずれでも、
目的は達成できますね。
ただ、怖いのはあるフィールドのコピーし忘れですね(リフレクションを使わない場合)。
後でフィールドを追加したりすると、
メソッドの修正も必要なのにそれを忘れてしまうことが良くあります。
こういったコピーし忘れの問題に似たものとしては、
シリアライズを Serializable でやるか Externalizable でやるか
の違いと似た話かもしれません。
Serializable はリフレクションを使っているのでコピーし忘れがないですが、
でも後者にも利点はありますし、そもそもフィールドの扱い忘れは、
単なるケアレスミスと片付けてしまえる問題かもしれません(本当にそうか?)。

ちなみに、私は、好み的には、
リフレクションを使わずに済ませることができるなら、
チマチマとフィールドをコピーするほうが好きです。
シリアライズも Externalizable のほうを選びます。
トリックスター
大ベテラン
会議室デビュー日: 2003/04/16
投稿数: 104
投稿日時: 2003-05-06 20:00
こんにちは。

unibon様

>>Serializeが関係ありそうな気がしますが、ファイルに一旦書き出し
>>たりしたくないです。
>
>これについては、
>java.io.ByteArrayOutputStream
>java.io.ByteArrayInputStream
>などを使うとメモリ上でストリームを扱えます。
>別の掲示板ですが、これらのクラスを使った例を書いたことがあります。
>http://www.vc-net.ne.jp/~ytp/bbs/java/bbs9205.html

教えていただいて、ありがとうございます。
たいへん参考になりました。

>ただし、シリアライズの機能を使ったとしても、
>class A のインスタンスを class B のインスタンスとして
>コピーすることはできないように思います。

やってみましたが、うまくいきませんでした。
結局は、ClassCastExceptionです。
たいへん見苦しいのですが、最後にソースを載せてみます。
無論、デバッグして下さいという意味ではありません。

>Object クラスの clone メソッドもやはり、
>できないようです(class A のインスタンスは
>class A のインスタンスとしてのみコピーできる)。

そうですか。
clone()は、むやみに手を出すのは危険と判断し、パスしております。

>私も copy メソッドを自前で作るのが良いと思います。
>ちなみにこのメソッドは class A に作っても class B に作ってもいずれでも、
>目的は達成できますね。

ベテランの方の賛同をいただけると心強いです。
ちなみに私が「ユーティリティ的な扱いとしたい」と書いたのは、
class A でも class B でもなく、第3のclass を指しております。

>ただ、怖いのはあるフィールドのコピーし忘れですね(リフレクションを使わない場合)。
>後でフィールドを追加したりすると、
>メソッドの修正も必要なのにそれを忘れてしまうことが良くあります。

今回は、それが恐くて、パフォーマンスよりも保守性をとりました。
ま、これは設計思想や個人の好みや時と場合によりますね。

Serializable と Externalizable のお話もたいへん興味深かったです。
いろいろ勉強になりました。
ありがとうございました。

−−−−−−−−
import java.io.*;

class A implements Serializable {
private String field1;
private String field2;
public String getField1() { return( field1 ); }
public String getField2() { return( field2 ); }
public void setField1( String value ) { field1 = value; }
public void setField2( String value ) { field2 = value; }
}

class B extends A implements Serializable {
private String fieldX;
public String getFieldX() { return( fieldX ); }
public void setFieldX( String value ) { fieldX = value; }
}

public class Widening {
static public void main( String[] args ) {
A objectA = new A();
objectA.setField1( "1" );
objectA.setField2( "2" );

try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( objectA );
oos.close();
byte[] b = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream( b );
ObjectInputStream ois = new ObjectInputStream( bais );

B objectB = ( B )ois.readObject();
objectB.setFieldX( "X" );

ois.close();

System.out.println( objectB.getField1() );
System.out.println( objectB.getField2() );
System.out.println( objectB.getFieldX() );
} catch( Exception x ) {
System.out.println( x );
}
}
}
Kensaku
常連さん
会議室デビュー日: 2003/02/16
投稿数: 22
投稿日時: 2003-05-07 00:09
こういう方法ではダメでしょうか?

コード:
import java.io.*; 

abstract class A implements Serializable { // abstractにする。
    private String field1; 
    private String field2; 
    public String getField1() { return( field1 ); } 
    public String getField2() { return( field2 ); } 
    public void setField1( String value ) { field1 = value; } 
    public void setField2( String value ) { field2 = value; } 
    public static A getInstance() { return new B(); } // インスタンスメソッドを作る。
} 

class B extends A implements Serializable { 
    private String fieldX; 
    public String getFieldX() { return( fieldX ); } 
    public void setFieldX( String value ) { fieldX = value; } 
} 

public class Widening { 
    static public void main( String[] args ) { 
        A objectA = A.getInstance(); // インスタンスメソッドでインスタンスを得る。
        objectA.setField1( "1" ); 
        objectA.setField2( "2" ); 
        
        try { 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
            ObjectOutputStream oos = new ObjectOutputStream( baos ); 
            oos.writeObject( objectA ); 
            oos.close(); 
            byte[] b = baos.toByteArray(); 
            ByteArrayInputStream bais = new ByteArrayInputStream( b ); 
            ObjectInputStream ois = new ObjectInputStream( bais ); 
            
            B objectB = ( B )ois.readObject(); 
            objectB.setFieldX( "X" ); 
            
            ois.close(); 
            
            System.out.println( objectB.getField1() ); 
            System.out.println( objectB.getField2() ); 
            System.out.println( objectB.getFieldX() ); 
        } catch( Exception x ) { 
            System.out.println( x ); 
        } 
    } 
} 

H2
ぬし
会議室デビュー日: 2001/09/06
投稿数: 586
お住まい・勤務地: 港
投稿日時: 2003-05-07 07:14
あまり、Serializableに頼らないほうがいいかと思います。Effective Javaにも載っているように、本当に必要がなければSerializableはするべきでないと思います。Serializableはこういう操作のためにあるわけではなく、あまりお勧めしません。

将来の拡張の可能性、ケアレスミス予防、Serializableに頼らないという利点で、個人的にはやっぱりReflectionなんですが、Reflectionを使うとそこまでパフォーマンスが落ちるのでしょうか?疑問です。
トリックスター
大ベテラン
会議室デビュー日: 2003/04/16
投稿数: 104
投稿日時: 2003-05-07 10:16
おはようございます。

Kensaku様
自分にはない発想だったんで、ちょっと感激しました。
でも、私がやりたいこととは、少し違いました。
class A から class B の存在を意識したくないのです。
class B を継承する class C が出現し同じ事をしたい
時、大幅な改修が入ってしまいます。
しかし、この手法は今後使えそうなので、憶えておきます。
ありがとうございました。
ちなみにこの場合、Serializeはあまり意味がないですね。
B objectB = ( B )objectA;
で、事足りますね。

H2様
Serializableについてのご意見、ごもっともだと思います。
今回、実験的な意味合いが強いので、ご容赦ください。
Reflectionのパフォーマンスについては、内部でやってることを想像する
と、いかにも遅いイメージがありますが、Javaそのものが設計上遅かったり、
でも最近はハードウェアの性能がよくて気にならなかったり、WEB環境では
通信速度と比較すれば、気にならない遅さだったりしますので、個人的には
そんなに神経質にならなくてもよいのかなと思っています。

−まとめ−
いろいろ考えて、今回私がやろうとしたことは、Javaの思想にそぐわなか
ったんだと思います。(もちろん皆さんに提示していただいた方法はそれ
ぞれ問題解決でありましたが、)それを裏技的に崩せないかと試みました
がJavaの鉄壁の防御にひれ伏した、といったところです(大袈裟)。
よいプログラミング言語だと思います。
zaxx_MD
大ベテラン
会議室デビュー日: 2003/04/21
投稿数: 204
お住まい・勤務地: 千葉県柏市
投稿日時: 2003-05-07 14:41
>ただし、reflectを使っているのでパフォーマンスを気にする時には
>使わないほうがいいでしょう。

「気にするときには」です。
ユーザレスポンスが重要なアプリケーションで
ほとんど全てのロジックから頻繁に呼び出される部分で
reflection実装をするのは何も考えてないのと同じですよね。

そのような限定された条件がなければreflectionは「使っても良い」実装だと思います。
(strutsは使っているようですし)

ただし読み難いコードになるので積極的に使うべきではなく、
今回のように明確な理由がある場合のみ使うようにしなければなりません。
ボア
ベテラン
会議室デビュー日: 2002/05/22
投稿数: 78
投稿日時: 2003-05-17 01:40
ちょっと時間がたってしまって申し訳ないのですが、
つい最近、似たような状況になってしまって...
ちょっとご相談なのですが、

HIRさん
> Aにcopy()メソッドを実装しておいて、Bのコンストラクタで呼び出すのはどうですか?
> ※以下、かなり端折ってますが・・・

これと似たような方法で、クラスAのコンストラクタにクラスAを
引数として持つものを用意する方法を教えてもらったのですが、
これって設計上問題ありますか?
例えば、

class A{
private Object field1;
private Object field2;

public A(A a){
this.field1 = a.getField1();
this.field2 = a.getField2();
}
}

class B extends A
{
public B(A a)
{
super(a);
}
}

使う時は、

B b = new B(A);

になります。
もちろん、A のコンストラクタで、参照渡しにするか
ちゃんとコピーするかという問題もありますが。

以上、よろしくお願いします。
asip
ベテラン
会議室デビュー日: 2001/12/27
投稿数: 77
投稿日時: 2003-05-17 09:53
引用:

YOU@ITさんの書き込み (2003-04-22 08:50) より:
ちょっと外しているかもしれませんが、継承ではなく
クラスBでクラスAをラップするようにしては?

class B {
private A a;
private String field;

public B(A a){
this.a = a;
}

public void setField(String s){ ... }
public String getField(){ ... }

// aへの転送メソッドを列挙
}

使うときは

A a = new A():
a.setXxx( ... );
....
B b = new B(a);
b.setField( ... );

のような感じで。




aへの転送メソッドをクラスBに列挙しなくてもよいと思います。

class B {
private A a;
private String field;

public B(A a){
this.a = a;
}

puublic A getA(){
return this.a;
}

public void setField(String s){ ... }
public String getField(){ ... }

}

A a = new A():
a.setXxx( ... );
....
B b = new B(a);
b.setField( ... );
//Aへ転送したい場合には
b.getA().setXxx( ... );

これだと、クラスAに変更があってもクラスBを変更する必要はありません。

[ メッセージ編集済み 編集者: asip 編集日時 2003-05-17 09:57 ]

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