Search code examples
scalacopyclone

Scala object cloning (copying) without value re-valuation


I have big object:

case class BigObject(val str: String, val number: Int) {
val someVal = ...
val someVal2 = ...
    }

I'd like to copy this object without re-valuation of values. Is it possible? Right now I'm using this approach:

val newBigObject = oldBigObject.copy(str = newStr)

As I see from the logs/debugger, "someVal" and "someVal2" are re-valuated. Is it possible to avoid it? As my BigObject is really big and value re-valuation takes some time, but performance is really important for me.

Thanks for your answers!


Solution

  • Here's a way:

    Make the someVal and someVal2 fields which are also passed to the constructor and pull out the initialization logic for those fields in the companion object.

    In your case:

    class BigObject private(val str: String,
                            val number: Int,
                            val someVal: SomeType,
                            val someVal2: SomeType) {
    
       def copy(newStr: String = str, newNumber: Int = number) = {
         new BigObject(newStr, newNumber, someVal, someVal2)
       }
    }
    
    object BigObject {
    
      def apply(str: String, number: Int): BigObject = {
        val someVal = initialize() //your initialization logic here
        val someVal2 = initialize2()
        new BigObject(str, number, someVal, someVal2)
      }
    
    }
    

    Now, you can copy without re-evaling the inner fields:

    val bigObj = BigObject("hello", 42)
    val anotherBigObj = bigObj.copy(newStr = "anotherStr")
    

    Alternatively, if you don't like companion objects, you can make two constructors. The primary one includes all the fields (also the non visible ones) and will be private. The public one will have only the two visible parameters:

    class BigObject private(val str: String,
                            val number: Int,
                            val someVal: Any,
                            val someVal2: Any) {
    
      def this(str: String, number: Int) = this(str, number, initializeVal, initializeVal2)
    
      def copy(newStr: String = str, newNumber: Int = number) = {
        new BigObject(newStr, newNumber, someVal, someVal2)
      }
    
    }
    

    Usage:

    val bigObj = new BigObject("hello", 42)
    val anotherBigObj = bigObj.copy(newStr = "anotherStr")