Reputation: 12662
If I want to modify one single parameter in a constructor.
In the Scala case class, the apply method will be overridden twice. Unless apply applies ( no pun ) to auxiliary constructor. Related to
How one can modify one single input from a constructor ?
Criteria :
The class must hold immutable data. All data must be accessible.
Note it doesn't have to be case class.
Additionally , no need to use the apply method.
no extra unused parameters. In the example below, _fistName
is still accessible but unused.
case class Person( lastName: String, _fistName: String, ... ) { lazy val fistName = _fistName.toLowerCase }
Upvotes: 1
Views: 3199
Reputation: 16832
Class parameters are not necessarily class members. You can have class parameters that do not become class members.
Method 1
class Foo(bar0: String) {
val bar = bar0.toLowerCase()
}
@main
def main(): Unit = {
println(Foo("AsDfGh").bar)
}
prints:
asdfgh
and the decompiled code is:
public class Foo {
private final String bar;
public Foo(final String bar0) {
this.bar = bar0.toLowerCase();
}
public String bar() {
return this.bar;
}
}
You see, bar0
is a "temporary" value, it does not become a field because it is not referenced.
So if you want to change a value, just do not use the original value in the methods.
Method 2
For case classes, it does not seem to work in 2022, but here is another trick:
case class Point (x: Double, y: Double)
class PolarPoint(r: Double, alpha: Double) extends Point(r*Math.cos(alpha), r*Math.sin(alpha))
Here r
and alpha
do not become members of PolarPoint
.
If you don't need two types, you can make the 1st constructor protected
:
case class Foo protected(x:Int)
class FooImpl(x0:Int) extends Foo(myFunc(x0))
You will reference objects as Foo
s but create FooImpl
s.
Method 3
Your class can have multiple parameter lists and implicit
s:
class Qux(x:String)(implicit val y:String = x.toLowerCase())
is converted to:
public class Qux {
private final String y;
public static String $lessinit$greater$default$2(String var0) {
return Qux$.MODULE$.$lessinit$greater$default$2(var0);
}
public Qux(final String x, final String y) {
this.y = y;
}
public String y() {
return this.y;
}
}
You see that here only y
becomes a field.
Upvotes: 0
Reputation: 9698
Here are two simple approaches.
Using class:
class Person(first: String, last: String) {
val firstName = first.toLowerCase
val lastName = last.toLowerCase()
}
val person = new Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Using trait + object's apply():
trait Person {
val firstName: String
val lastName: String
}
object Person {
def apply(first: String, last: String) = new Person {
override val firstName: String = first.toLowerCase
override val lastName: String = last.toLowerCase
}
}
val person = Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Note that classes must be instantiated with new
, while traits (created with apply()
) don't.
Why no case classes? Well, they are designed to serve as ADTs (abstract data types). Basically, they are thought of as containers for some data without any logic. That's why they get apply()
out-of-the-box. If you want to override it, then means your class doesn't have the semantics of a case class.
I can see that @prayag took the effort of trying to help you force it into a case class, but seriously, if it doesn't have to be one (and you said so in the question), then don't make it one.
Upvotes: 3
Reputation: 31192
The reference you had posted seems to have lot of answers as well.
Two simple ways I could think of
make it abstract case class
and define companion object which would mutate the value you want
define the member of case class as var
and mutate it.
eg. (using scalatest
)
class CaseClassSpecs extends FunSpec {
describe("case class modify") {
it("APPROACH 1 : modifies abstract case class member") {
object Item {
def apply(itemId: String, itemName: String) :Item = new Item(itemId.toLowerCase, itemName) {}
}
abstract case class Item private (val itemId: String, itemName: String)
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
it("APPROACH 2 : modifies case class member which is var") {
case class Item (var itemId: String, itemName: String) {
itemId = itemId.toLowerCase()
}
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
}
}
Upvotes: 1