Scala - avoid override of a super class field in the copy method

128 views Asked by At

Let's say for instance that I have the following classes, is there a way to do the copy command without overriding the argument from the superclass?

I want to avoid override val arg1: String from the declaration of - case class MyCaseClass(override val arg1: String, arg2: String) extends MyAbstractClass

abstract class MyAbstractClass (val arg1: String = "bar 1")

case class MyCaseClass(arg2: String) extends MyAbstractClass

object MyMain {
  def main(args: Array[String]): Unit = {
    val myObj = MyCaseClass(arg2 = "bar 2")
    println("a.arg1: " + myObj.arg1) // a.arg1: bar 1
    println("a.arg2: " + myObj.arg2) // a.arg2: bar 2

   val b = myObj.copy(arg1 = "try_1", arg2 = "try_2") 

//>> "too many arguments (2) for method copy: (arg2: String)com.MyCaseClass
//>> Note that 'arg1' is not a parameter name of the invoked method."
   
  }
}

Edit: In my actual code I got hundreds of fields, that's why it's so important, I want to keep clean code and avoid hundreds of meaningless redundant overrides.

2

There are 2 answers

0
Maor Aharon On

As @Mateusz Kubuszok suggested, that's might be the only option, which obviously doesn't make the code cleaner.

abstract class MyAbstractClass (val arg1: String = "bar 1")

case class MyCaseClass(val arg2: String) extends MyAbstractClass {

  def copy(newArg1: String, newArg2: String): MyCaseClass =
    new MyCaseClass(arg2 = newArg2) {
      override val arg1: String = newArg1
    }

}

object MyMain {
  def main(args: Array[String]): Unit = {

    val myObj = MyCaseClass(arg2 = "bar 2")
    println("myObj.arg1: " + myObj.arg1) // myObj.arg1: bar 1
    println("myObj.arg2: " + myObj.arg2) // myObj.arg2: bar 2

   val mySecObj = myObj.copy(newArg1 = "try_1", newArg2 = "try_2")
    println("mySecObj.arg1: " + mySecObj.arg1) // mySecObj.arg1: try_1
    println("mySecObj.arg2: " + mySecObj.arg2) // mySecObj.arg2: try_2

  }
}
0
Mateusz Kubuszok On

An alternative would be to NOT provide a value to arg1

trait MyAbstractClass {
  val arg1: String // no value assigned...
}

case class MyCaseClass(
  arg1: String = "bar 1", // ...no need for override val
  arg2: String
) extends MyAbstractClass

Then

val myObj = MyCaseClass(arg2 = "bar 2")
println("a.arg1: " + myObj.arg1) // a.arg1: bar 1
println("a.arg2: " + myObj.arg2) // a.arg2: bar 2

val b = myObj.copy(arg1 = "try_1", arg2 = "try_2")

works without any issue. The only difference is that default value would have to be provided for arg1 in MyCaseClass constructor.

If you don't want to make arg1 a part of:

  • MyCaseClass.apply
  • MyCaseClass.unapply
  • myCaseClass.toString
  • myCaseClass.equals(anotherObject)
  • myCaseClass.hashcode

then probably using a case class is a bad idea in the first place, since case class is less of a "universal boilerplate generator" and more like a build-in way of defining records with a strong opinion what a record is. And there are alternatives to it like e.g. contraband with different opinions and decisions.