Case classes with inheritance and default parameters - scala

Is there an elegant way in Scala to use case classes (or case classish syntax) with inheritance and default constructor parameters? I kind of want to do this (without repeating the default parameters of the superclass):
abstract class SuperClazz(id: String = "")
case class SubClazz(name: String = "", size: Int = 0) extends SuperClazz
val sub = SubClazz(id = "123", size = 2)

I would say it's not possible to do without repeating parameters from super class. It is due to the fact that case classes are special type of scala classes. It is beacuse compiler implicitly generates companion extractor object with apply and unapply methods and in those methods it will be no parameter that is not specified in class parameters.
Consider this code snippet
abstract class SuperClazz(id: String = "")
class SubClazz(name: String,id: String) extends SuperClazz {
override def toString: String = "name: " + name + ",id: " + id
}
object SubClazz {
def apply(name: String = "", id: String = "") = new SubClazz(name, id)
}
It's shorter and simpler ( and little bit different regarding toString method ) version of what is being created when
case class SubClazz(name: String, id: String) extends SubClazz
is called.

Related

Scala instantiate common val on two classes with same parent

I have two classes that extend the same trait :
trait Event {
val propertyCommon : String = ""
}
case class EventA(propertyA : String) extends Event
case class EventB(propertyB : String) extends Event
Now I instantiate my classes in a List :
myList : List[Event] = List(EventA("a"),EventB("b"))
What I want to do is to instantiate after the common property, as if there was a copy method in my trait :
myList.map(_.copy(propertyCommon = "Something"))
How could I do that ?
What you say you want to do is called a prototype pattern in OOP, and as far as I can tell it isn't supported out of the box in Scala (and I think anywhere else).
There are also a few problems:
propertyCommon is not set up using constructor, so each implementation that would have to set it, would most likely have to override it using anonymous class (but then, this property will not be a part of equals, hashcode, toString, derivation, etc)
Event doesn't define any interface that would allow updating it
The easiest (and safest) way I see to implement functionality you want, would be something like this:
trait Event {
val propertyCommon: String // will be set by implementing class
def clone(propertyCommon: String = this.propertyCommon): Event
}
// children define propertyCommon to match the interface
case class EventA(propertyA: String, propertyCommon: String = "") extends Event {
def clone(propertyCommon: String = this.propertyCommon): EventA = copy(propertyCommon)
}
case class EventB(propertyB: String, propertyCommon: String = "") extends Event {
def clone(propertyCommon: String = this.propertyCommon): EventB = copy(propertyCommon)
}
However, probably if we knew more about your problem we could provide some other, simpler solution.

Case class immutable still able to change the parameter values

I reviewed some code from a colleague and I came across a case class which is by default immutable.
the below case class can be changed so my question is how is this possible since case classes are immutable but in this construct i can change the case class parameters?
case class RegisterCustomerRequest(`first-name`: String,
`last-name`: String,
`house-details`: String,
street: String,
zipcode: String,
city: String
extends WcRequestData {
def this(cardHolderData: CardHolderData,
registrationCode: RegistrationCode,
customerNumber: Long,
cardDesignImageId: String) =
this(`first-name` = cardHolderData.firstname,
`last-name` = cardHolderData.lastname,
street = cardHolderData.streetAndNumber,
zipcode = cardHolderData.zipCode,
city = cardHolderData.city,
# `house-details` =
s"${if (cardHolderData.employerName.contains("&"))
cardHolderData.employerName.replace("&" , " & ") else " /
"}${cardHolderData.employerName} ")#
}
why can I define a def this method which can change the values of parameters. What is this construct good for is this good coding style?
The case class RegisterCustomerRequest is still immutable however it has an auxiliary constructor def this which allows it to be constructed in a different way. For example, given
case class User(name: String)
case class Foo(name: String) {
def this(user: User) {
this(name = user.name)
}
}
we can construct Foo like so
Foo("picard")
or using the auxiliary constructor
new Foo(User("picard"))
In both cases the result is an immutable object. To confirm immutability try reassigning name after construction
(new Foo(User("picard"))).name = "worf" // Error: reassignment to val
As suggested by som-snytt, we can define apply method on companion object instead of auxiliary constructor like so
object Foo {
def apply(user: User): Foo = Foo(user.name)
}
which enables the following construction
Foo(User("picard"))

Modify one value in a Scala class constructor in a concise way

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
Modifying case class constructor parameter before setting value
How to override apply in a case class companion
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 }
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.
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")
}
}
}
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 Foos but create FooImpls.
Method 3
Your class can have multiple parameter lists and implicits:
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.

Scala inheritance default parameter in parent class

I have an abstract class with a default value for its parameter.
I don't want to have to reuse the default value in the constructor of all the possible implementations.
abstract class Place(val place: String = "World")
class Message(val message: String = "Hello", p: String) extends Place(p) {
override def toString = s"$message $place"
}
What I want to get
new Message("Hi", "Universe") = "Hi Universe" // Ok
new Message("Hi") = "Hi World" // Doesn't work, second parameter is required
new Message() = "Hello World" // Doesn't work, second parameter is required
I considered using an auxiliary constructor omitting the second parameter, but it doesn't help since you can't call super constructors outside of the main constructor.
I want to know how to do it, or why it is not possible. I'm not looking for a workaround, like not using inheritance.
I'm afraid that is not possible. Quite simply, you ARE passing a value to Place constructor, so it wont use the default, whatever its value might be. If you don't mind having a var instead of a val, here is a variant that works for your 3 cases:
abstract class Place(var place: String = "World")
class Message(val message: String = "Hello") extends Place()
{
def this(message: String, place: String) = {
this(message)
this.place = place
}
override def toString = s"$message $place"
}
Constructors in Scala are a little of a mess IMHO. Sometimes a better answer is just to use factory apply() methods on a companion object, which are more flexible.
You can reuse the default value in a more elegant way:
object Place {
val defaultPlace = "World"
}
abstract class Place(val place: String = Place.defaultPlace)
class Message(val message: String = "Hello", p: String = Place.defaultPlace) extends Place(p) {
override def toString = s"$message $place"
}

Mixing dynamic objects with structural typing in Scala

Is it possible to do this? (I'm using scala 2.10) To call a method that requires that the object has a function named "fullName", but the object being built with the Dynamic trait. The compiler complains, but maybe I'm doing it wrong.
I don't need this for any job, I'm just learning the language.
import scala.language.dynamics
object Main extends App {
class MyStatic {
private var privateName = ""
var lastName = ""
def name_= (n: String) {
privateName = n
}
def name = s"***$privateName***"
def fullName = s"$name $lastName"
}
class MyDynamic extends scala.Dynamic {
val map = collection.mutable.Map[String, String]()
def selectDynamic(key: String): String = map(key)
def updateDynamic(key: String)(value: String) {
map += key -> value
}
def applyDynamic(key: String)(value: Any*) = key match {
case "fullName" => {
val name = map("name")
val lastName = map("lastName")
s"$name $lastName"
}
}
}
def showFullName(o: { def fullName: String }) = s"My full name is $o.fullName"
println("Starting App...")
val s = new MyStatic
s.name = "Peter"
s.lastName = "Parker"
println(showFullName(s))
val d = new MyDynamic
d.name = "Bruce"
d.lastName = "Wayne"
println(showFullName(d))
}
The structural type { def fullName: String } basically means "any type with a no-arg method named fullName returning a String.
MyDynamic has no such method, and thus does not comply with this structural type. The fact that MyDynamic extends scala.Dynamic is irreleveant: it means that for any instance of it, you can perform what looks like a call to fullName, but it does not mean that MyDynamic (as a type) has any such member.
So the short answer is no, you cannot mix dynamic objects with structural typing like that.
For completeness, I must add that it could be made to work as you expected, but it would require a special provision from the compiler (namely, the compiler could consider than any type extending scala.Dynamic -- and implementing the required lookup methods -- is compatible with any structural typing, and implement the call not via reflection as is normally done, but by calling the corresponding lookup method).
You are trying to glue together two completely different things. While structural typing is also sometimes compared to 'duck-typing', its feature is exactly the use of static type information (even if on the use site the byte code will call reflection). Per definition, your dynamic type does not have such static type information. So you will never be able to convince the Scala compiler that your dynamic type has a method that can be statically verified to exist.
The only workaround would be to allow any type in showFullName and use reflection to call fullName (again, I'm not sure if this plays out with a dynamic object).
On the other hand, Scala will let you do anything with dynamic types, handing the responsibility over to you:
def showFullName(o: Dynamic) = s"My full name is $o.fullName"
println(showFullName(d))