If I would like to use Java's Externalizable interface in a Scala class, no problem,
import java.io.Externalizable
import java.io.ObjectInput
import java.io.ObjectOutput
class ExternalizableTestClass(
var str: String = "",
var int: Int = 52
)
extends Externalizable
{
// Scala version of null constructor for Externalizeable.
def this() = this("null construction value", 52)
override def toString : String
= "ExternalizableTestClass(str:" + str + ", int:" + int + ')'
override def writeExternal(out: ObjectOutput) {
out.writeUTF(str)
out.writeInt(int)
}
override def readExternal(in: ObjectInput) {
str = in.readUTF()
int = in.readInt()
}
}
It will also carry data through externalizing. Run this through a externalizing/de-externalizing test rig,
val obj = new ExternalizableTestClass("ohh", 87)
Returns,
After de-externalizing object: ExternalizableTestClass(str:ohh, int:87)
All good.
Add a subclass, then,
class ExternalizableTestSubclass(
str: String = "",
int: Int = 64
)
extends ExternalizableTestClass (
str,
int
)
with Externalizable
{
def this() = this("subclass null construction value", 74)
override def writeExternal(out: ObjectOutput) {
// Java way to externalize if there is a subclass
super.writeExternal(out)
}
override def readExternal(in: ObjectInput) {
// Java way to externalize if there is a subclass
super.readExternal(in)
}
}
It will also carry data through externalizing. Run this through a externalizing/de-externalizing test rig,
val obj = new ExternalizableTestSubclass("ohh", 87)
Returns,
After de-externalizing object: ExternalizableTestClass(str:ohh, int:87)
Note we're not seeing any null construction values (as I might hope, externalizable code is populating the class).
Rarely a use for subclasses that have no variation, so lets make it do something; add an abstract method to the super-class, realise in the sub-class, make it a toString so we see what is going on,
abstract class ExternalizableTestClass(
var str: String = "",
var int: Int = 52
)
extends Externalizable
{
def this() = this("null construction value", 52)
override def toString : String
= "ExternalizableTestClass(str:" + str + ", int:" + int + ')'
def methodVariedBySubclass : String
override def writeExternal(out: ObjectOutput) {
out.writeUTF(str)
out.writeInt(int)
}
override def readExternal(in: ObjectInput) {
str = in.readUTF()
int = in.readInt()
}
}
class ExternalizableTestSubclass(
str: String = "",
int: Int = 64
)
extends ExternalizableTestClass (
str,
int
)
with Externalizable
{
def this() = this("subclass null construction value", 74)
def methodVariedBySubclass : String
= "ExternalizableTestSubclass(str:" + str + ", int:" + int + ')'
override def writeExternal(out: ObjectOutput) {
super.writeExternal(out)
}
override def readExternal(in: ObjectInput) {
super.readExternal(in)
}
}
Run this through a serializing/deserializing test rig,
val obj = new ExternalizableTestSubclass("ohh", 87)
Returns,
After de-externalizing object: ExternalizableTestClass(str:ohh, int:87)
After de-externalizing object with methodVariedBySubclass: ExternalizableTestSubclass(str:subclass null construction value, int:74)
Oh dear. The toString() method defined on the base is as expected, but the method methodVariedBySubclass() (a toString()) is returning tthe null parameters.
What, you may wonder, was the situation before serialization? Answer:
Before de-externalizing object: ExternalizableTestClass(str:ohh, int:87) Before de-externalizing object with methodVariedBySubclass: ExternalizableTestSubclass(str:ohh, int:87)
Ah.
Has anyone been here? Throw in some insider knowledge?
(I've tried some variations to see if they expose any mechanisms. Making the superclass non-externalizable, and calling from the subclass returns the same dissimilar results.)