prosseek
prosseek

Reputation: 191109

Scala's case class and its constructor parameter passing to super classes

In Scala, I can use the parameter in subclass to the superclasses as follows:

abstract class A(var a:Int)
class B(a:Int) extends A(a)
class C(a:Int) extends B(a)

object Main extends App {
    val b = new C(10)
    println(b.a)
    b.a = 100
    println(b.a)
}

However, with case class, I have errors:

abstract class A(var a:Int)
class B(a:Int) extends A(a)
case class C(a:Int) extends B(a) // case generates a as val

object Main extends App {
    val b = C(10)
    println(b.a) // without val, a is not accessible
    b.a = 100
    println(b.a)
}

This is the message:

case_simple.scala:8: error: reassignment to val
    b.a = 100
        ^

I see that it's caused by case class makes the constructor parameter immutable, but I have errors with C(var a:Int)

case_simple.scala:3: error: overriding variable a in class A of type Int;
 variable a needs `override' modifier
case class C(var a:Int) extends B(a) // case generates a as val
                 ^
one error found

And with C(override var a:Int)

case_simple.scala:3: error: overriding variable a in class A of type Int;
 variable a cannot override a mutable variable
case class C(override var a:Int) extends B(a) // case generates a as val

How to solve this issue?

Upvotes: 3

Views: 2230

Answers (1)

Aivean
Aivean

Reputation: 10882

1.

When you have construction like this:

 abstract class A(var a: Int)
 class B(a: Int) extends A(a)

a in class B is the constructor parameter that is passed to the parent constructor of A. It can be rewritten as this for clarity:

 abstract class A(var a: Int)
 class B(param: Int) extends A(param)

param is not a member of class B, B still has only one variable a derived from class A.

2. In scala it is impossible to override var. It seems that it doesn't make sense, but I can't find clear explanation.

Now to the point.

Case classes have special treatment for parameters. The syntax is limited, you can't define the parameter of case class that will not be the member of the class (either val or var). Taking into account two statements above, your only choice is to introduce another field in case class and then pass it to the parent constructor. It creates redundancy and is not good approach:

abstract class A(var a: Int)
class B(a: Int) extends A(a)
case class C(param: Int) extends B(param)

As a conclusion, I suggest you to avoid mixing case classes with vars. Case classes are designed to be small immutable DTOs. You can use copy method of case class to conveniently change one or more fields while staying immutable:

case class C(a: Int)

val b = C(10)
println(b.a)
val b1 = b.copy(a = 100)
println(b1.a)

Upvotes: 1

Related Questions