Javaの外部化可能インターフェイスのガイド
1. 前書き
このチュートリアルでは、we’ll have a quick look at java’s java.io.Externalizable interface。 このインターフェイスの主な目的は、カスタムシリアル化と逆シリアル化を容易にすることです。
先に進む前に、必ずserialization in Javaの記事を確認してください。 次の章では、このインターフェースでJavaオブジェクトをシリアル化する方法について説明します。
その後、java.io.Serializableインターフェースと比較した主な違いについて説明します。
2. The Externalizableインターフェース
Externalizableは、java.io.Serializableマーカーインターフェイスから拡張されます。 Any class that implements Externalizable interface should override the writeExternal(), readExternal() methods。 このようにして、JVMのデフォルトのシリアル化動作を変更できます。
2.1. 直列化
この簡単な例を見てみましょう。
public class Country implements Externalizable {
private static final long serialVersionUID = 1L;
private String name;
private int code;
// getters, setters
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(code);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.code = in.readInt();
}
}
ここでは、Externalizableインターフェースを実装し、上記の2つのメソッドを実装するクラスCountryを定義しました。
In the writeExternal() method, we’re adding the object’s properties to the ObjectOutput stream.これには、Stringの場合はwriteUTF()、int値の場合はwriteInt()などの標準メソッドがあります。
次に、readUTF(), readInt()メソッドを使用してfor deserializing the object, we’re reading from the ObjectInput streamが、書き込まれたのとまったく同じ順序でプロパティを読み取ります。
serialVersionUIDを手動で追加することをお勧めします。 これがない場合、JVMは自動的に追加します。
自動生成された数値はコンパイラに依存します。 これは、起こりそうもないInvalidClassExceptionを引き起こす可能性があることを意味します。
上記で実装した動作をテストしてみましょう。
@Test
public void whenSerializing_thenUseExternalizable()
throws IOException, ClassNotFoundException {
Country c = new Country();
c.setCode(374);
c.setName("Armenia");
FileOutputStream fileOutputStream
= new FileOutputStream(OUTPUT_FILE);
ObjectOutputStream objectOutputStream
= new ObjectOutputStream(fileOutputStream);
c.writeExternal(objectOutputStream);
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream
= new FileInputStream(OUTPUT_FILE);
ObjectInputStream objectInputStream
= new ObjectInputStream(fileInputStream);
Country c2 = new Country();
c2.readExternal(objectInputStream);
objectInputStream.close();
fileInputStream.close();
assertTrue(c2.getCode() == c.getCode());
assertTrue(c2.getName().equals(c.getName()));
}
この例では、最初にCountryオブジェクトを作成し、それをファイルに書き込みます。 次に、ファイルからオブジェクトを逆シリアル化し、値が正しいことを確認します。
印刷されたc2オブジェクトの出力:
Country{name='Armenia', code=374}
これは、オブジェクトの逆シリアル化に成功したことを示しています。
2.2. 継承
クラスがSerializableインターフェースから継承する場合、JVMはサブクラスからもすべてのフィールドを自動的に収集し、それらをシリアル化可能にします。
これはExternalizableにも適用できることに注意してください。 We just need to implement the read/write methods for every sub-class of the inheritance hierarchy.
前のセクションのCountryクラスを拡張する以下のRegionクラスを見てみましょう。
public class Region extends Country implements Externalizable {
private static final long serialVersionUID = 1L;
private String climate;
private Double population;
// getters, setters
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeUTF(climate);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
super.readExternal(in);
this.climate = in.readUTF();
}
}
ここでは、2つの追加プロパティを追加し、最初のプロパティをシリアル化しました。
we also called super.writeExternal(out), super.readExternal(in) within serializer methods to save/restore the parent class fields as wellに注意してください。
次のデータを使用して単体テストを実行してみましょう。
Region r = new Region();
r.setCode(374);
r.setName("Armenia");
r.setClimate("Mediterranean");
r.setPopulation(120.000);
デシリアライズされたオブジェクトは次のとおりです。
Region{
country='Country{
name='Armenia',
code=374}'
climate='Mediterranean',
population=null
}
since we didn’t serialize the population field in Region class, the value of that property is null.に注意してください
3. ExternalizableとSerializable
2つのインターフェースの主な違いを見てみましょう。
-
シリアル化の責任
ここでの主な違いは、シリアル化プロセスの処理方法です。 クラスがjava.io.Serializableインターフェースを実装する場合、JVMはクラスインスタンスのシリアル化に全責任を負います。 In case of Externalizable, it’s the programmer who should take care of the whole serialization and also deserialization process.
-
使用事例
オブジェクト全体をシリアル化する必要がある場合は、Serializableインターフェイスの方が適しています。 一方、for custom serialization, we can control the process using Externalizable。
-
パフォーマンス
java.io.Serializableインターフェースはリフレクションとメタデータを使用するため、パフォーマンスが比較的遅くなります。 比較すると、Externalizable interface gives you full control over the serialization process.
-
読書順序
While using Externalizable, it’s mandatory to read all the field states in the exact order as they were written.それ以外の場合は、例外が発生します。
たとえば、Countryクラスのcodeプロパティとnameプロパティの読み取り順序を変更すると、java.io.EOFExceptionがスローされます。
一方、Serializableインターフェースにはその要件はありません。
-
カスタムシリアル化
フィールドをtransientキーワードでマークすることにより、Serializableインターフェイスでカスタムシリアル化を実現できます。 The JVM won’t serialize the particular field but it’ll add up the field to file storage with the default value。 そのため、カスタムシリアル化の場合はExternalizableを使用することをお勧めします。
4. 結論
Externalizableインターフェースのこの短いガイドでは、主要な機能、利点について説明し、簡単な使用例を示しました。 また、Serializableインターフェースとの比較も行いました。
いつものように、チュートリアルの完全なソースコードはover on GitHubで入手できます。