@transient lazy val field serialization

10,978

There's a couple of tickets on this in Scala's Trac:

I'd advise you to test against a trunk build of 2.9, as it may already be fixed.

Share:
10,978

Related videos on Youtube

hydrocul
Author by

hydrocul

Updated on April 29, 2022

Comments

  • hydrocul
    hydrocul about 2 years

    I have a problem on Scala. I serialize an instance of class with @transient lazy val field. And then I deserialize it, the field is assigned null. I expect the lazy evaluation after deserialization. What should I do?

    Following is a sample code.

    object Test {
    
      def main(args: Array[String]){
    
        //----------------
        // ClassA - with @transient
        //----------------
    
        val objA1 = ClassA("world");
    
        println(objA1);
        // This works as expected as follows:
        //   "Good morning."
        //   "Hello, world"
    
        saveObject("testA.dat", objA1);
    
        val objA2 = loadObject("testA.dat").asInstanceOf[ClassA];
    
        println(objA2);
        // I expect this will work as follows:
        //   "Good morning."
        //   "Hello, world"
        // but actually it works as follows:
        //   "null"
    
    
    
        //----------------
        // ClassB - without @transient
        // this works as expected
        //----------------
    
        val objB1 = ClassB("world");
    
        println(objB1);
        // This works as expected as follows:
        //   "Good morning."
        //   "Hello, world"
    
        saveObject("testB.dat", objB1);
    
        val objB2 = loadObject("testB.dat").asInstanceOf[ClassB];
    
        println(objB2);
        // This works as expected as follows:
        //   "Hello, world"
    
      }
    
      case class ClassA(name: String){
    
        @transient private lazy val msg = {
          println("Good morning.");
          "Hello, " + name;
        }
    
        override def toString = msg;
    
      }
    
      case class ClassB(name: String){
    
        private lazy val msg = {
          println("Good morning.");
          "Hello, " + name;
        }
    
        override def toString = msg;
    
      }
    
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
    
      def saveObject(fname: String, obj: AnyRef){
        val fop = new FileOutputStream(fname);
        val oop = new ObjectOutputStream(fop);
        try {
          oop.writeObject(obj);
        } finally {
          oop.close();
        }
      }
    
      def loadObject(fname: String): AnyRef = {
        val fip = new FileInputStream(fname);
        val oip = new ObjectInputStream(fip);
        try {
          oip.readObject();
        } finally {
          oip.close();
        }
      }
    
    }
    
  • hydrocul
    hydrocul over 13 years
    Thank you very much. I'll try 2.9.
  • tksfz
    tksfz over 9 years
    Just an update that this works fine in Scala 2.10.4. However, I notice it's still broken if the initializer relies on a by-name argument. For example, make ClassA.name be => String and it won't work. (You'll also need to make it a non-case class that extends Serializable, and make name a val.)