How to inherit class in scala [duplicate] - scala

This question already has an answer here:
If case class inheritance is prohibited, how to represent this?
(1 answer)
Closed 5 years ago.
I am trying to inherit a class in Scala. My Parent class is
case class Person (name:String, age:Int, valid:Boolean)
My child class is
case class MarriedPerson (override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
When I try this, I get an error saying
:13: error: case class MarriedPerson has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
Why is this the case and how I get around this to get a case class to inherit another case class?
If I remove the "case" in the parent class, I get an error saying
that
:15: error: value name overrides nothing
override val name: String,
Why can't a case class not inherit from a normal class in this case?

You cannot inherit from one case class to another. In theory its possible to inherit from a case class to a non-case class, but that's not recommended (which is why it's a good practice to make your case classes final - more on the topic in this post).
Case classes have, among other peculiarities, that of exposing publicly all of the constructor arguments. This does not happen for normal classes, which is why to make your snippet of code work you should just prepend all of Person's arguments with val.
Thus, the following is valid syntax:
class Person(val name: String, val age: Int, val valid: Boolean)
case class MarriedPerson(
override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
However, for your information, it is also quite common to use traits for this kind of situations, which allows for greater flexibility (traits support multiple inheritance) and a slightly less verbose syntax, as follows:
trait Person {
def name: String
def age: Int
def valid: Boolean
}
case class MarriedPerson(
name: String,
age: Int,
valid: Boolean,
spouse: Person) extends Person
If you are curious on why it's possible to define defs in traits that are overridden by vals in sub-classes, it's because Scala implements the Uniform Access Principle.

Related

Are constructor parameters treated differently in classes vs. case classes?

In the book, "Programming in Scala 5th Edition", it is mentioned in the fourth chapter that we can create class' objects using the following way if a class is a case class:
scala> case class Person(name: String, age: Int)
// defined case class Person
scala> val p = Person("Sally", 39)
val p: Person = Person(Sally,39)
I do not find it different from the following normal way:
scala> class Person(name: String, age: Int)
// defined class Person
scala> val p = Person("Aviral", 24)
val p: Person = Person#7f5fcfe9
I tried accessing the objects in both the cases and there was a difference. When I declare the same class as a case class, I can access its members: p.name, p.age whereas if I try to do the same with a normally declared class, I get the following error:
1 |p.name
|^^^^^^
|value name cannot be accessed as a member of (p : Person) from module class rs$line$3$.
How are the two cases different as far as constructing an object is considered?
As the Tour Of Scala or the Scala 3 book says
When you create a case class with parameters, the parameters are public vals.
Therefore, in
case class Person(name: String, age: Int)
both name and age will be public, unlike in an ordinary class.

use dynamic return type in a scala fuction

I have a function that gets a string with name of model and returning an instance of that case class, but im wondering what is the correct way to use dynamic return type here in this function?
case class Person(fname: String, lname: String)
case class Animal(type: String, weight: Double)
def getInstanceOf(model: String): ??? = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
thanks!
It may help to have a bit more context of what the end goal is here to suggest a solution.
If your models all conform to a particular classification and you don't need to know exactly what instance they are then the abstract class (or trait) suggestion above should work.
EDIT: my original answer included this snippet:
You could also make the return type generic and put a type boundary on it in this scenario, such as:
def getInstanceOf[T <: Living](model: String): T = ...
But as pointed out below this allows the caller to specify what type they expect which may counteract what is contained in the model argument
If you only returned one of two possible types but needed the exact type then maybe an Either[T1, T2] would be appropriate here (http://www.scala-lang.org/api/current/scala/util/Either.html) such as:
def getInstanceOf(model: String): Either[Person, Animal] = ...
But again I think it would be easier to answer if the end goal was explained a bit
Maybe you can define an abstract class and extends both classes from it, and then you can specify the abstract class as the return type:
abstract class Living
case class Person(fname: String, lname: String) extends Living
case class Animal(Type: String, weight: Double) extends Living
def getInstanceOf(model: String): Living = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
// getInstanceOf: (model: String)Living

Ducktyping implicit conversion in Scala

Is it possible to have a duck-typing style conversion where a converter will accept an arbitrary object and will try to convert it to the destination type?
case class RequiredFields(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(object: ????): Person = Person(object.name, object.surname, None)
Arbitrary object doesn't have to be related to RequriedField class in any way, just the fields must exist.
I think you can combine structural typing and implicit conversions. Like so:
type RequiredFields = {
def name: String
def surname: String
}
case class NotAPerson(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(o: RequiredFields): Person = Person(o.name, o.surname, None)
val p:Person = NotAPerson("oh", "my")
Are you sure that's a good idea, though? I think the much better pattern here would be to define a type class with the required fields. Have you considered that?
As hasumedic answered you probably want to use a trait.
If for some reason you don't want to use a trait (you don't have control over the package objects that are being sent to you etc) you can also use reflection to get the fields. (I don't actually know if this is a good idea, but my best guess is that it isn't)
case class RequiredFields(name: String, surname: String)
val rF = new RequriedFields("Hey", "yo")
val method = rF.getClass.getDeclaredMethod("name")
Then you get your value by invoking the getter for that field that scala makes for you
scala> val x = method.invoke(rF).asInstanceOf[String]
x: String = Hey
I think that you're looking for a trait:
trait Nameable {
def name: String
def surname: String
}
case class Person(name: String, surname: String, age: Option[Int]) extends Nameable
implicit def arbitraryToPerson(variable: Nameable): Person = Person(variable.name, variable.surname, None)
You can think of a trait like an interface. A characteristic of Scala traits is that they also accept implementations of its methods.
Classes extending this trait will need to implement the methods name and surname. In this case, the only valid Nameable parameter would be a Person. Having more classes that extend the trait would allow for some sort of polymorphism, making this implicit actually useful.
EDIT
From your comment and negative, just to point out that you can call your traits however you like. In your case, something like Nameable could work. It doesn't mean that there's a hierarchy, but that your object fulfils certain conditions, such as implementing those methods. Classes can extend more than one trait in Scala, using the with keyword.
case class Dog(name: String, surname: String) extends Animal with Nameable
In this case, your dog would be an animal, but you can enforce the implementation of the Nameable methods by extending the trait.
It seems you can do ducktyping driven conversion in the following way:
implicit def convertToPerson(obj: {def name(value: String): String; def surname(): String}): Person = {
Person(obj.name, obj.surname, None)
}

Extending a scala class with less ceremony

I am probably doing something wrong.
When I extend a class, I specify the mapping between the extendee's constructor and the extended class constructor:
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base(one, two)
How can I instead suffice with something like any of the following, thus implying a "default" mapping?
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base
class Base(one: String, two: String)
case class Extended(three: String) extends Base
I am probably missing the cleaner way of just adding a parameter without all that ceremony.
Or should I be using a trait rather than subclassing, for such simple thing....
All the parameters of a case class generated apply method have to be specified in the case class declaration, so your second proposal cannot work. The first one can be accomplished if Base is abstract, using abstract vals:
abstract class Base {
// no initializers!
val one : String
val two : String
def print { println("Base(%s, %s)" format (one, two)) }
}
// Note: constructor arguments of case classes are vals!
case class Extended(one: String, two: String, three: String) extends Base
...
Extended("foo", "bar", "baz").print // prints 'Base(foo, bar)'
However, the names need to match exactly.

Generate constructor for superclass in Scala

Given simple class hierarchy
abstract sealed class Base(id: String)
case class Child1(id: String, value: Int) extends Base(id)
case class Child2(id: String, value: Long, file: File) extends Base(id)
can I use macros or something like that to avoid passing id to Base (and instruct compiler to generate this for me)? With single argument it's not that hard to pass it, but in case of several arguments it becomes uncomfortable.
Or if I could omit specification of id in child classes and make compiler generating ones for me from base class?
You can make Base as a trait:
sealed trait Base {
val id: String
}
case class Child1(id: String, value: Int) extends Base
case class Child2(id: String, value: Long, file: File) extends Base