Here is the way:
Create someVal and someVal2 , 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()
Now you can copy without re-zeroing the internal fields:
val bigObj = BigObject("hello", 42) val anotherBigObj = bigObj.copy(newStr = "anotherStr")
Alternatively, if you don't like companion objects, you can create two constructors. Primary includes all fields (also not visible) and will be closed. Public will have only 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) } }
Using:
val bigObj = new BigObject("hello", 42) val anotherBigObj = bigObj.copy(newStr = "anotherStr")