かとじゅんの技術日誌

技術の話をするところ

Serializableの飼いならし方

さて、Javaシリアライズを改めて復習してみる。
まずこのあたりから。まぁ、よくわからん。。。

java.io
インタフェース Serializable

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)

Effective Java

Effective Javaによい事例があったので呼んでみることにする。『項目74 Serializableを注意して実装する』あたりから。何を注意すればいよいのかな。。。

ポイント1 Serializableを実装したクラスの構造は柔軟性を欠く

クラスをシリアライズ(ディシリアライズ)に対応させるにはSerializableインターフェイスを実装すればよいということはよく知られていると思います。これはJavaが持つデフォルトのシリアライズ形式を使用した場合の話です。
Serializableインターフェイスはマーカーインターフェイスといって実装を強要するメソッドが定義されていません。要するに空っぽのインターフェイス。Serializableインターフェイスを実装しているかどうかの目印にしか使っていないのでマーカーという意味。

Serializableを実装する際の主要なコストは、一旦リリースされるとクラスの実装を変更する柔軟性を低下させることです。

Serializableはメソッドの仕様が定義されていないので、実装クラスで対応するメソッドを実装しなくてよく、インターフェイスに対して実装クラスの構造は比較的制約がないように思われがちです。ですが、デフォルトのシリアライズ形式だと、クラスのプライベートおよびパッケージプライベートのインスタンスフィールドは公開APIと同等の扱いになる。

public class Fuga implements Serializable{

    private int hoge;
    private String muga;

    // getter, setter省略
}

というクラスでシリアライズしたデータファイルを

public class Fuga implements Serializable{

    private String hoge2;
    private int muga2;

    // getter, setter省略
}

でディシリアライズできないということですね。当然のことなんですがこれはアプリケーションを長期間において利用していくには非常に重要な話題ですね。

ポイント2 バグやセキュリティホールの可能性の増大

Serializableを実装する際の2つ目のコストは、バグやセキュリティポールの可能性を増大させることです。

何を言っているかというと、ディシリアライズは、他のコンストラクタと同様にオブジェクトを生成する「隠れたコンストラクタ」。デフォルトのシリアライズ形式の場合はこのコンストラクタは見えないので、正確にいうと不変性や不正アクセスを容易にさらしてしまうことになる。つまり、通常のコンストラクタにはインスタンス生成時の契約を実装できるが、隠れたコンストラクタにはその契約はどうにも実装できないwということになります。どうやらこれはカスタマイズ形式のシリアライズで意図的にreadObjectを実装することで回避できるようです。

ポイント3 新しいバージョンがテストの負荷を増大させる

Serializableを実装する際の3つ目のコストは、新しいバージョンのクラスをリリースすることに関連したテストの負荷を増大させることです。

新しいバージョンのインスタンスシリアライズして、古いバージョンのインスタンスでディシリアライズできるか、またその逆が可能かという視点でテストの負荷が増大するという話。テスト量はクラス数とリリースした数の積に比例するとかで負荷が増大。互換性には、バイナリレベルと、セマンティックレベルがある模様。バイナリとはシリアライズされたデータがきちんとでシリアライズできることで、セマンティックとは意味レベル?ロジック的な互換性ですかね。それらを検査する必要があると。

Serializableは軽く考えてはいかんw

改めて復習してみるといろいろ制約事項がありますね。詳しくはEffective Javaを読んでね。こういう下りもあります。

Serializableインターフェイスを実装することは、軽く考えて決めることではありません。

要するに上記のようなコストを考えながら実装しなければならないということだと思います。