- ベストアンサー
SerializableインターフェースのserialVersionUIDについて
- serialVersionUIDとは、直列化オブジェクトの送信側と受信側が直列化互換性を検証するために使用されるバージョン番号です。
- 送信側と受信側は、オブジェクトを保存しているファイル(ストリーム)側と、オブジェクトの読み出しを行うプログラム側を指します。
- serialVersionUIDの値は単純な値でも問題ありませんが、割り振り方によってはより良い結果が得られることもあります。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
> 回答文中の受信側JavaVMにロードされたクラスHogeBeanは > serialVersionUIDが「-2L」となっているので > ・・・・これを「-1L」として、・・・・ > serialVersionUIDを同じにするとどうなるのでしょうか? 良いところに気づきましたね。そこも前回敢えて端折った部分です。 結局、Serializableインタフェースの実装も、シリアルバージョンも、 「人間がコンピュータが判断しやすいようにしてあげる」ための物で、 人間にとってはほとんど何の意味も持たないものなんだね~。 ・バージョン同じで中身同じ→普通。 ・バージョン違いで中身同じ→JavaVMは違うものと判断。 ●バージョン同じで中身違い→JavaVMは同じものと判断し、後で何らかの例外が起こる事でしょう。 ・バージョン違いで中身違い→JavaVMは違うものと判断。 >>それは誤解で、Serializableインタフェースを実装しなくてもシリアライズは可能です。 > JavaDocには・・・・ですが、 > anmochi様がおっしゃっている・・・・より一般的な「シリアライズ」と > いうものを考えた場合の話でしょうか? そうです。私の発言はJavaという仕組みではなく、シリアライズの概念と捕らえてください。Sun JavaVMは確かにif(hoge instanceOf Serializable)のチェックを行っています。 ただ、これはあくまで「Sun標準のシリアライザー」での話であり、 シリアライザーも当然自作する事ができますので、その際に if(hoge instanceOf Serializable)をチェックするかどうかは シリアライザー作成者の意識次第です。 Serializableインタフェースが、メソッド宣言も定数も何も無いのに なんでこんなのある、つまりSunは用意したのだろうと思いませんでしたか? 何も中身がないインタフェースでも、 instanceOf SerializableがTrueかFalseかという違いが出てきます。 これが、Serializableインタフェースが存在する理由です。 とにかく、ここでは前述の通り、Serializableインタフェースの実装は JavaVMに対して「このクラスはシリアライズ可能だよ」と教えてあげる ヒントであり、それ以上でもそれ以下でもないという事を改めて述べただけです。 Serializableインタフェースそのものの存在に意味は無い。 「人間がそこに意味を持たせている」だけな訳です。 >>Serializableインタフェースを実装しシリアライズ不可能なクラス、というのも当然作れちゃう。 > JavaにおいてSerializableインタフェースを実装していながら > シリアライズができないパターンというのは > 具体的にどういうソースなのでしょうか? では、そもそも「シリアライズ不可能」とは何か考えてみましょう。 もっとも分かりやすい例だと、ファイルオブジェクトの インスタンスを保持(has-A)するようなクラスはシリアライズできません。 これは、ファイルオブジェクトはOSのファイルディスクリプタと 結び付けられ、ファイルディスクリプタを含めてシリアライズして 別の場所でデシリアライズしても、そのファイルディスクリプタで表される OSがオープンしているファイルが存在するとは限らないからです。 これが「シリアライズ不可能」という事。状態が自分の中で閉じていないという事ですね。 で、再び前述の通り、Serializableインタフェースの実装というのは、 「本当にそのクラスがシリアライズ可能かどうか」とは何の関係もありません。 クラスの作者が恣意的につけるものです。 繰り返しになりますが、 ・Serializableインタフェースの実装は単なる宣言に過ぎない。 ・本当にシリアライズ可能かどうかはクラスの作成者しか分からない。 ・JavaVMがシリアライズ可能性の精査をするのはコストがかかる。 ・シリアライズ可能性はクラス作者に宣言させてそれを信じるしかない。 というか信じるという仕組みの方がみんなが楽でしかも速い。 というのが、Javaにおけるシリアライズ精査の仕組みだという事です。 つまり、クラスの作成者の善意というか正義に任せようという わりといい加減というか、人間くさい仕組みですね。
その他の回答 (1)
- anmochi
- ベストアンサー率65% (1332/2045)
これでもはしょりすぎで全然説明ができていないのですが、あまりにも長文になってしまったので一旦これでポストします。後は個別に補足・お礼と回答のやり取りでやっていけば良いかなと。 > 「オブジェクトを保存しているファイル > (あるいはそのストリーム)側」が「受信側」で > 「そのオブジェクトの読み出しを行っているプログラム側」が > 「受信側」という理解で正しいでしょうか? 恐らく最初の受信側は送信側の事でしょうが、中間ファイルとデシリアライザーが一対になるでしょうか? 送信者とは「シリアライザーでシリアライズを行った人」という意味で、受信者は「デシリアライザーでデシリアライズを行った人」です。 だって、シリアライズの目的はインスタンスの状態を保持してそれをいつか元に戻すという一連の処理なのですから、クラスの中身を何かにする処理と、何かからクラスに戻す処理が一対になるはずでしょう? で、それらの処理は、違うJavaVM上で動く事もありまして、その時クラスの情報はそれぞれのJavaVMが持っています。これが一致しなかったら一大事。 仮に、以下のようなエンティティBeanを考えてみましょう。 public HogeBean implements Serializable { private static final serialVersionUID = -1L; public int age; public String name; } このBeanをインスタンス化してシリアライズしましょう。ここでは、仮にシリアライズされるとXML文書の形式になる事とします。 <HogeBean serialVersionUID="-1"> <age>19</age> <name>Anmochi</name> </HogeBean> 今、これが「シリアライズされたインスタンス」ですね。これがストリームによってファイルに保存されたり、ネットワーク上に流されたりする訳です。これが送信側。 次に、デシリアライズして使える状態(インスタンス)に戻さないとせっかくシリアライズした意味がありません。受け手でデシリアライズしましょう。受信側です。これまた仮に、受信側のJavaVMにロードされたクラスHogeBeanが以下のようになっていたらどうでしょうか? public HogeBean implements Serializable { private static final serialVersionUID = -2L; public int age; public String firstname; public String familyname; } おお! 中身が全然違う! ドラクエで言えば「おお、勇者よ、型が合わないとは情けない。」くらいの勢いですよ。これだと、デシリアライズできません。クラスは設計図ですよね。ところが、シリアライズされたインスタンスは型情報を持っていないので(注:本当は持っています)、デシリアライズをうんせうんせとやった挙句に違う事が分かるわけです。これでは無駄にコストがかかる。この場合、どっちのクラス構造が誤りなのかは問題ではありません。一致しない事が問題なのです。なのでバージョンIDを変えておけばする一発で(不一致という事実が)分かるという訳ですね。 > serialVersionUIDというのは読み出したオブジ・・・・ 現在ではserialVersionUIDは無くても動作します。その時の動作は詳しく検証していませんが、ランダムな数値が選ばれるのでしょうかね。あるいは-1L? > 上記理解が正しい場合、serialVersionUIDというのは「1L」といった > 単純な値でも特に問題ないのでしょうか? 問題ないと思います。 > 自動で割り振る機能があるそうですが > これを利用するメリットは何でしょうか? 自分で考えなくて良い。 > シリアライズする場合、なぜSerializableインターフェースを > 載せなければならない仕様になっているのでしょうか? それは誤解で、Serializableインタフェースを実装しなくてもシリアライズは可能です。ソースを見てこのクラスがシリアライズ可能かどうか、という判断を、「人間は」すぐ行えます。ところが、コンピュータが判断すると、それはとてもコストがかかる処理になります。なので、コンピュータがif(hoge instanceOf Serializable)の一発で判断できるように、人間様がコンピュータに教えてあげるという訳で、コンピュータ側からみたら先ほどのinstanceOf以上でも以下でもないのです。なので、Serializableインタフェースを実装しシリアライズ不可能なクラス、というのも当然作れちゃう。 > 最初からすべてのクラスはシリアライズ可能にしておくと > 何か不都合があるのでしょうか? ロジックしか記述されていないクラスなど、そもそもシリアライズの必要がないクラスはシリアライズできなくしておく方が良いと個人的には思う。技術的には前述の通り理論上どんなクラスでもシリアライズは可能だが、シリアライズする必要が無い場合は明示的にシリアライズさせない仕組みを作っておく方が良いと思うよ。
お礼
丁寧にひとつずつ回答いただきありがとうございます。 >> 「オブジェクトを保存しているファイル >> (あるいはそのストリーム)側」が「受信側」で >恐らく最初の受信側は送信側の事でしょうが ご推察の通り、先の「受信側」は「送信側」と書いたつもりでした。 誤記です。 大変申し訳ありません。 >>‥バージョンIDを変えておけばする一発で(不一致という事実が)分かるという訳ですね。 丁寧な回答をありがとうございます。 バージョンIDの使われ方がよくわかりました。 しかし回答を拝見させていただいているなかで新たな疑問が生まれました。 回答文中の受信側JavaVMにロードされたクラスHogeBeanは serialVersionUIDが「-2L」となっているので 違うバージョンであることがすぐわかるということですが、 これを「-1L」として、クラスの構造が異なる送信側と serialVersionUIDを同じにするとどうなるのでしょうか? >>それは誤解で、Serializableインタフェースを実装しなくてもシリアライズは可能です。 JavaDocには 「クラスの直列化可能性は、java.io.Serializable インタフェースを実装したクラスによって有効になります。このインタフェースを実装していないクラスでは、その状態が直列化または直列化復元されることはありません。」 とあるのですが、anmochi様がおっしゃっている「インタフェースを実装しなくてもシリアライズは可能」というのは Javaという枠内の話ではなく、より一般的な「シリアライズ」というものを考えた場合の話でしょうか? >>Serializableインタフェースを実装しシリアライズ不可能なクラス、というのも当然作れちゃう。 上記と併せて、私の理解不足のためにこの部分もよく分かりませんでした。 JavaにおいてSerializableインタフェースを実装していながらシリアライズができないパターンというのは 具体的にどういうソースなのでしょうか? 教えてもらってばかりで大変恐縮ではありますが、 ご指導の程何卒よろしくお願い申し上げます。
お礼
お礼が遅くなり申し訳ありません。 非常に詳しい解説をしていただきましてありがとうございます。 おかげさまで疑問となって詰まっていたものが キレイさっぱり解けました。 本当にありがとうございました。