1

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.)

Community
  • 1
  • 1

1 Answers1

0

Hah, I have an answer. It's nothing to do with Extenalizable, or Scala's abbreviated constructors (or inner classes, or name-mangling). It's a clash of identifiers fooling the Scala compiler. Rename all identifiers in the base class (or the subclass). You may want to refer to the answers to this question,

Scala style guideline for underscore in identifiers

For anyone interested, with this adjustment, the above is working code.

Bear in mind that all (known to me) Java serialization techniques are available, or can be made available, in Scala,

https://github.com/eishay/jvm-serializers/wiki

Community
  • 1
  • 1