From: "NAKAMURA, Hiroshi" Date: 2003-10-14T11:26:56+09:00 Subject: [ruby-list:38543] Re: marshal_dump (was Re: ) なひです。 > From: "Yukihiro Matsumoto" > Sent: Monday, October 13, 2003 8:32 PM > |ちなみにJavaでは、dumperが、クラス階層内の各クラスの > |writeObjectメソッドをそれぞれ呼んでくれるので(2種類あるserialize > |インタフェイスの手軽なほう)、各クラスが、自分で定義した > |インスタンス変数(とは呼ばないけど)を書き出すことで、救えます。 > |writeObjectメソッドが定義されてない場合、デフォルトで全ての > |インスタンス変数(同)が書き出されます。 > > えーと、writeObjectメソッドはサブクラスでoverrideされていよ > うがなんだろうが個別に呼ばれるんですか。 はい。writeObjectとreadObjectはそのように使われます(後述の readResolveは異なります)。 ちなみに > |writeObjectメソッドが定義されてない場合、デフォルトで全ての > |インスタンス変数(同)が書き出されます。 と書いてしまいましたが、正しくは「〜定義されてない場合、デフォルトで そのクラスで定義されている全てのインスタンス変数が〜」でした。 > Rubyではあんまりそう > いうのはやってないんで考えてませんでしたけど、それが良いと言 > うなら検討の余地はあります。 夏にまつもとさんに直接お話した時には、Rubyではそのように呼び分けるのは できないのかななんて思っていたのですが、最近、SOAPのmarshallingコードに 関連して、そのような方法を中田さんに教えてもらったのでした。 UnboundMethodなんて初めて使った。。。 良いことかどうかは、なひにもちょっとよくわからなくなってきています。 債務分割という点ではよさげなんですが、基本的にmarshal_dumpペアを 定義したクラスを継承する、なんてレアなケースだと思いますし、 複雑さを考えると避けるべき設計だと思います。ただ、それでもどうしても 継承したくなった場合、子が親のシリアライズフォーマットまで規定しないと いけないというのは厳しい。。親クラスは他人が書いていて、いつ何時 インスタンス変数が増えたり、シリアライズフォーマットが変更されるかも。。 というわけでよくわかりません。苦労している方のコメントをお待ちします。 ちなみに、債務を分割する場合、各クラスにおいて、そのクラスで導入した インスタンス変数が取り出せると便利です。例えばmarshal_dumpペアが 定義されていないクラス階層では、rubyが、デフォルトで、そのクラスで 導入されたインスタンス変数のみを書き出してあげるとか。 class Foo def initialize @foo = STDERR end def marshal_dump # @fooは書き出さない end end class Bar < Foo def initialize super @bar = 123 end end class Baz < Bar def initialize super @baz = STDOUT end def marshal_dump # @bazは書き出さない end end Marshal.load(Marshal.dump(Baz.new)) # @barのみ保存されて欲しい 無理でしょうか。 > |Rubyでは、インスタンス変数がどのクラスで定義されているか、 > |あまり意識してない人が多いような気がします(?)。よって咳さんの > |書かれてるように、Javaみたいにクラス階層をたどることで債務分割を > |するのでなく、一番下位のmarshal_dump/marshal_loadを定義するヤツが > |気をつけて全部やれ、とするのもアリだと思います。 > > 現時点ではそうなってますね。 はい。 > |関連してひとつ、別の提案ですが、Javaでは、とあるメソッド(readResolve)を > |定義しておくと、loadの後に呼んでくれて、そいつが返したオブジェクトを元の > |オブジェクトとすりかえる、という機能があります。singletonやenumな > |クラスで、loaderが生成した新規オブジェクトを、singletonオブジェクトと > |すりかえられます。 > > すいません。Javaについてあまり知識が無いので、「loadの後に呼 > んでくれて」、「元のオブジェクトとすりかえる」という記述が具 > 体的にはイメージできませんでした。もうちょっと説明していただ > けませんか? えーと、readResolveはJDK/1.2から追加されたインタフェイスで、 つまり1.1までのフォーマットと実装しかよく知らないなひは 弱いのですが(言い訳)、 class Foo implements Serializable { private static final Foo singleton = new Foo(); public static final Foo getInstance() { return singleton; } private Foo() {} private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); } private Object readResolve() { return singleton; } } こんなことをしておくと、Fooのserialized formatを読み込む際、 readObjectで新たなFooインスタンスが生成されたあと、そいつの readResolveメソッドが呼ばれ、返ってきたsingletonが、読み込む オブジェクトグラフ中に代わりに埋め込まれます。readObjectで 作られた新たなFooインスタンスはすぐに捨てられます。 readResolveが呼ばれるタイミングは、serialize formatを読み込んで いく最中、readObjectを呼んでオブジェクトの構築が終わった直後です。 http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/input.doc6.html という話ではなく、もしかして実装方法でしょうか。 (現時点では)なひも実装は見てません。^^; readResolveについては、クラス階層を順に呼ぶのではなく、通常の メソッド呼び出し相当で1つだけ呼ばれます。該当クラスで定義されて いればそれが呼ばれ、該当クラスで定義されていない場合は、 親クラスでprivateとして定義されていれば呼ばれず、 protectedなら呼ばれます。