Wednesday, July 22, 2015

When Serializable and Externalizable appear in the same class hierarchy

In my post Difference between Serializable and Externalizable, we know that a Java class can choose the object serialization mechanism by either implementing java.io.Serializable or  java.io.Externalizable. The question is what is the behavior if some of the parent classes of an object implement Serializable and some other parent classes implement Externalizable?

Given the class diagram for Programmer as below:


And the implementation for

Person 

import java.io.Serializable;

public class Person implements Serializable {
    int age;
}   

Employee

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Employee extends Person implements Externalizable {
    int salary;
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(salary);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        salary = in.readInt();
    }
}

Programmer

import java.io.Serializable;

public class Programmer extends Employee implements Serializable {
    int yearsOfExp;
}

In the program below, we create a Programmer instance and assign a value to each of its fields. Note that neither of its fields is transient or static, they are all eligible for serialization. When we execute the program below, guess what is the result.

public class App {
    public static void main(String[] args) throws Exception {
        Programmer programmer = new Programmer();
        programmer.age = 30;
        programmer.salary = 6000;
        programmer.yearsOfExp = 8;
        
        serialize(programmer, "programmer.ser");
        
        programmer = (Programmer) deserialize("programmer.ser");
        System.out.println(programmer.age 
                + " " + programmer.salary 
                + " " + programmer.yearsOfExp);
    }
    
    public static void serialize(Object obj, String fileName)
            throws IOException {
        try (ObjectOutputStream out 
                = new ObjectOutputStream(new FileOutputStream(fileName))) {
            out.writeObject(obj);
        }
    }

    public static Object deserialize(String fileName)
            throws IOException, ClassNotFoundException {
        try (ObjectInputStream in 
                = new ObjectInputStream(new FileInputStream(fileName))) {
            Object obj = in.readObject();
            return obj;
        }
    }
}

Result:
0 6000 0
Only the salary get serialized.

Answer

The existence of Externalizable in any level of the class hierarchy will supersedes Serializable. This means that serialization runtime will call the overrode Externalizable read/write method instead of automatically serialize/deserialize the target object's fields.

So what can we do if we want to serialize all Programmer's fields?

As Programmer extends Employee, it is Externalizable (Programmer to implements Serializable is redundant). The Programmer could override the Externalizable methods to read/write age and yearsOfExp.

public class Programmer extends Employee {
    int yearsOfExp;
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);
        out.writeInt(age);
        out.writeInt(yearsOfExp);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        super.readExternal(in);
        age = in.readInt();
        yearsOfExp = in.readInt();
    }
}

Re-run the App program again, and this time we will get the desired result as below:
30 6000 8
In short, whenever an Externalizable class appears in the class hierarchy, serialization runtime is expecting the programmer to handle the field serialization by him/herself.

No comments: