type erasure and Inheritance in scala - scala

i have following class hierarchy.
trait Item {val id: String}
case class MItem(override val id: String, val name: String) extends Item
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name)
val d = new DItem("1", "one", "another one")
println(d)
Expected Output
DItem(1, one, another one)
Actual Output
Mitem(1,one)
Why is this happening. What is recommended so that i get the real type of my object and the not type of super class.

This is not a type erasure. You are getting this result because DItem gets toString() implementation inherited from Mitem. You have to override it to get what you want
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
So here is a result:
scala> val d = new DItem("1", "one", "another one")
d: DItem = DItem(1, one, another one)
scala> println(d)
DItem(1, one, another one)
It is almost always a bad idea to inherit from case classes because besides toString successor class will also inherit equals and hashCode.
Another drawback is limited pattern-matching for such successor classes i.e it is impossible to use such classes in case branches and may lead to confusing errors.
Example
case class A(id: String)
class B(id: String, name: String) extends A(id)
new B("foo", "bar") match {
case A(id) => println(id)
case other => println(other)
}
You may expect that there is no error in this code, but you'll get
<console>:17: error: constructor cannot be instantiated to expected type;
found : A
required: B
case A(id) => println(id)
^
However if you'll infer a type for B instance explicitly it will work
scala> new B("foo", "bar").asInstanceOf[A] match {
| case A(id) => println(id)
| case other => println(other)
| }
foo
So... Inheriting from case classes is very error-prone and confusing and should be avoided unless you know what are you doing.

Inheriting from case classes is deprecated as far as I know. So case classes can (should) only inherit from regular classes.

doing println usually invoke toString on the object pass on it.
so what happen on your code is, it will invoke the toString implementation of the object, it happens to be MItem that has this implementation.
so you need to override the toString on DItem like this:
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
if you want to just get the type of the object you can use getClass.
println(d.getClass)

Related

Scala: Default value for trait method not accepted

I have defined a method in a trait:
trait myTrait {
def myMethod(entity: Entity = null, name: String)
}
val t = ....//concrete implementation of myTrait
t.myMethod("John")
I have provided default value of null for entity in method definition.
This give compilation error as: Type mismatch, required MyCustomClass found String
Why is it so?
Use the name of your argument.
t.myMethod(name = "John")
It also works for case class when you want to .copy
case class MyStruct(a: String, b: String)
val a = MyStruct("foo", "bar")
val b = a.copy(b = "clazz")
See https://docs.scala-lang.org/tour/named-arguments.html
Also, in scala, the presence / absence of value is usually represented by Option[T] like
def myMethod(entity: Option[Entity] = None, ... )

Generic function for case class creation with tuple does not compile

I shall explain my question with example as shown below.
import scala.reflect.ClassTag
trait LivingBeing extends Product { def name:String; def age:Int}
case class Person (name:String, age:Int) extends LivingBeing
case class Cat(name: String, age:Int) extends LivingBeing
// usual way of creating a case class instance
val john = Person("john", 23)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
// create a generic function
def createLivingBeing[T<: LivingBeing](name:String, age:Int)(implicit evidence: ClassTag[T]): T = {
T tupled (name, age) // Does not compile; why?
}
How can one elegantly construct different case classes (that are of a certain trait) generically, given a type and values for its fields?
Consider type class solution
trait LivingBeingFactory[T <: LivingBeing] {
def apply(name: String, age: Int): T
}
object LivingBeingFactory {
implicit val personFactory: LivingBeingFactory[Person] =
(name: String, age: Int) => Person(name, age)
implicit val catFactory: LivingBeingFactory[Cat] =
(name: String, age: Int) => Cat(name, age)
}
def createLivingBeing[T <: LivingBeing](name:String, age:Int)(implicit evidence: LivingBeingFactory[T]): T = {
evidence(name, age)
}
createLivingBeing[Person]("Picard", 70)
// res0: Person = Person(Picard,70)
createLivingBeing[Cat]("Bob", 5)
// res1: Cat = Cat(Bob,5)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
...which is the equivalent of...
val garfield = (Cat.apply _).tupled(("Garfield", 8))
This, on the other hand...
T tupled (name, age) // Does not compile; why?
...produces Error: not found: value T because T is a type, not a value. Cat is both a type and a value. It is the type specified for the class, but it is also the companion object to the class Cat. All case classes have a companion object with an apply() method. The compiler knows the difference between them and it knows where one can be used/referenced but not the other.

Extend case class from another case class

I have two case class Person and Employee
case class Person(identifier: String) {}
case class Employee (salary: Long) extends Person {}
I am getting following error:
Unspecified value parameters: identifier: String
Error: case class Employee has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes
I am new to Scala and not able to understand what I have to do.
Version:
Scala : 2.11
Unfortunately, I'm afraid it is not possible for case class to extend another case class.
The inheritance in "plain" classes would look like:
class Person(val identifier: String) {}
class Employee(override val identifier: String, salary: Long)
extends Person(identifier) {}
val el = new Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
If you would like to achieve a similar effect with case classes, you would need to reach out to traits:
trait Identifiable {
def identifier: String
}
case class Person(identifier: String) extends Identifiable {}
case class Employee(identifier: String, salary: Long)
extends Identifiable {}
val el = Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
Defining extractors
Extractor provides a way for defining a matching statement used in pattern matching. It is defined in an object in unaply method.
Let's consider the first example again adding support for extractors:
class Person(val identifier: String)
class Employee(override val identifier: String, val salary: Long)
extends Person(identifier)
object Person {
def unapply(identifier: String): Option[Person] = {
if (identifier.startsWith("PER-")) {
Some(new Person(identifier))
}
else {
None
}
}
}
object Employee {
def unapply(identifier: String): Option[Employee] = {
if (identifier.startsWith("EMP-")) {
Some(new Employee(identifier, 999))
}
else {
None
}
}
}
Now, let's define a method that will define pattern matching using those extractors:
def process(anInput: String): Unit = {
anInput match {
case Employee(anEmployee) => println(s"Employee identified ${anEmployee.identifier}, $$${anEmployee.salary}")
case Person(aPerson) => println(s"Person identified ${aPerson.identifier}")
case _ => println("Was unable to identify anyone...")
}
}
process("PER-123-test") // => Person identified PER-123-test
process("EMP-321-test") // => Employee identified EMP-321-test, $999
process("Foo-Bar-Test") // => Was unable to identify anyone...
Case classes in Scala add several different features but often you really use only some of them. So the main question you need to answer is which features you really need. Here is a list based on the spec:
remove the need to type val before field names/constructor params
remove the need for new by adding apply method to the companion object
support for pattern matching by adding unapply method to the companion object. (One of nice things of Scala is that pattern-matching is done in a non-magical way, you can implement it for any data type without requiring it to be a case class)
add equals and hashCode implementations based on all the fields
add toString implementations
add copy method (useful because case classes are immutable by default)
implement Product trait
A reasonable guess of the equivalent for case class Person(identifier: String) is
class Person(val identifier: String) extends Product {
def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
override def equals(other: Any): Boolean = other match {
case that: Person => (that canEqual this) && identifier == that.identifier
case _ => false
}
override def hashCode(): Int = identifier.hashCode
override def toString = s"Person($identifier)"
def copy(newIdentifier: String): Person = new Person(newIdentifier)
override def productElement(n: Int): Any = n match {
case 0 => identifier
case _ => throw new IndexOutOfBoundsException(s"Index $n is out of range")
}
override def productArity: Int = 1
}
object Person {
def apply(identifier: String): Person = new Person(identifier)
def unapply(person: Person): Option[String] = if (person eq null) None else Some(person.identifier)
}
case class Employee(override val identifier: String, salary: Long) extends Person(identifier) {}
Actually the main objections to inheriting from a case class and especially making a case class inheriting another one are the Product trait, copy and equals/hashCode because they introduce ambiguity. Adding canEqual partially mitigates the last problem but not the first ones. On the other hand in a hierarchy like yours, you probably don't need the copy method or Product implementation at all. If you don't use Person in pattern matching, you don't need unapply as well. Most probably all you really need case for is apply, toString and hashCode/equals/canEqual.
Inheriting from case classes (even with regular non-case classes, which is not prohibited) is a bad idea. Check this answer out to get an idea why.
You Person does not need to be a case class. It actually does not need to be a class at all:
trait Person {
def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person

How can I extend an abstract class with an optional member in Scala?

I have an abstract base class, Foo, whose constructor I'd like to have an optional parameter. If none is provided, I'll just give it a None value.
A source Foo will not have parents, so I'd just like to construct them without a list of parents (leave default value for parent list)
A derived Foo might have provided parents, so I'd like to mimic the signature of the Foo base class.
Below is my attempt:
abstract class Foo(val id: String, var parentIds: Option[List[String]]=None) { }
case class SourceFoo(override val id: String)
extends Foo(id, parentIds=None) { }
case class DerivedFoo(override val id: String,
override var parentIds: Option[List[String]])
extends Foo(id, parentIds) { }
I'm getting a compiler error that a mutable variable cannot be overridden (referencing the parentIds in the DerivedFoo constructor.
This list is subject to change, so I don't want to make it a val (which removes my compiler issues).
This is a very basic OO issue, so it must be simpler than I seem to be making it. How can I achieve my desired behavior idiomatically?
I managed to fix this after reading the documentation:
The constructor parameters of case classes are treated as public values and can be accessed directly.
Since my base class is abstract, I can simply extend it with default, val construction.
I simply need to specify that parentIds is a var in the DerivedFoo constructor.
abstract class Foo(id: String, parentIds: Option[List[String]]=None) { }
case class SourceFoo(id: String) extends Foo(id) { }
case class DerivedFoo(id: String, var parentIds: Option[List[String]]=None)
extends Foo(id, parentIds) { }
Here is another probably better way to go about it. Explictly acknowledge the difference between class parameters and class members. You also can make them private members if you like following this block of code.
abstract class Foo(identifier: String, parentIdentifiers: Option[List[String]]) {
val id = identifier
var parentIds = parentIdentifiers
}
case class SourceFoo(override val id: String) extends Foo(id, parentIdentifiers = None) { }
case class DerivedFoo(identifier: String, parentIdentifiers: Option[List[String]]) extends Foo(identifier, parentIdentifiers) { }
After that, you can create DerivedFoo and refer to the members as you are probably expecting, and you won't have two members with different names.
REPL output:
scala> DerivedFoo("1", Some(List("200","201","202")))
res0: DerivedFoo = DerivedFoo(1,Some(List(200, 201, 202)))
scala> res0.parentIds
res1: Option[List[String]] = Some(List(200, 201, 202))
scala> res0.parentIds = Some(List("800", "801", "802"))
res0.parentIds: Option[List[String]] = Some(List(800, 801, 802))
I think you can achieve your goal by changing the name of the parameter in the abstract class as follows.
abstract class Foo(val id: String, var parentIdentifiers: Option[List[String]]) {
parentIdentifiers = None
}
case class SourceFoo(override val id: String)
extends Foo(id, parentIdentifiers = None) { }
case class DerivedFoo(override val id: String,
var parentIds: Option[List[String]])
extends Foo(id, parentIds) { }
For the mutation, you can import scala.collection.mutable and use mutable.ListBuffer instead of List.
I assume of course, that you won't change the parentIds of a DerivedFoo instance from Some to None.
This will allow you use vals but still have mutable state.
But I wouldn't say mutable state is idiomatic Scala.
You usually use immutable val and List, and just copy the object whenever you want to change the list.
val fooA = SourceFoo("a")
val fooB = DerivedFoo("b", "a" :: Nil)
val fooB2 = fooB.copy(parentIds = fooB.parentIds :+ "x")
So to be more idiomatic, the simplest you can do is
sealed abstract class Foo(val id: String, val parentIdsOpt: Option[List[String]])
case class SourceFoo(override val id: String)
extends Foo(id, None)
case class DerivedFoo(override val id: String, val parentIds: List[String])
extends Foo(id, Some(parentIds))
Which is pretty close to what you had.
Note that DerivedFoo.parentIds isn't Option anymore, because DerivedFoo always has parents, so you don't have to deal with Option. (You still have to deal with empty list, though)
Also note the sealed keyword on the trait, which is not required, but recommended if you want to match on an instance of the abstract class or trait. (You can use sealed only if you own all the sub-classes, which seems to be the case in your example)

Understanding breakdown of case classes

If I have case class defined as below
case class Calculator(brand: String, model: String)
How does it's companion object's unapply method would look like? What type of arguments would it take?
I am not able to emulate this by defining a class and then it's companion object by myself.
class abc (age:Int, name:String) {
}
object abc {
def apply(age:Int, name:String) = new abc(age, name)
def unapply(obj:abc) = Some("test")
}
abc(1, "aaaa")
res6: abc = abc#269f4bad
res6 match {
| case abc(1, "aaaa") => println("found")
| }
error: too many patterns for object abc offering String: expected 1, found 2
case abc(1, "aaaa") => println("found")
^
<console>:14: error: type mismatch;
found : Int(1)
required: String
case abc(1, "aaaa") => println("found")
You'll need to make age and name members of your class so that they are accessible after construction (can be done by making them vals), and then use them in unapply:
class abc (val age:Int, val name:String)
object abc {
def apply(age:Int, name:String) = new abc(age, name)
def unapply(candidate: abc) = Some((candidate.age, candidate.name))
}
Which would match correctly:
scala> new abc(2, "bbbb") match {
| case abc(1, "aaaa") => println("found 1")
| case abc(2, "bbbb") => println("found 2")
| case _ => println("not found")
| }
found 2
Tzach beat me to it..
Your error message btw. is because of the mismatch between the Option your unapply returns (which contains a single string) and the match in your case statement (with wants to match against a pair).
You could include a null check just to make sure (the compiler does, for case class companion objects):
class Abc(val age: Int, val name: String)
object Abc {
def unapply(obj: Abc): Option[(Int, String)] =
if (obj == null)
None
else
Some((obj.age, obj.name))
}
Shameless self-promotion: If you're interested in more details of pattern matching, you might find my little presentation "Everything you always wanted to know about pattern matching" useful.
If you want to create a custom class which has a unapply method and want to use the Extractor Pattern with it, the following rules should apply:
The return type of an unapply should be chosen as follows:
If it is just a test, return a Boolean. For instance case even()
If it returns a single sub-value of type T, return an Option[T]
If you want to return several sub-values T1,...,Tn, group them in an optional
tuple Option[(T1,...,Tn)].
Generally, this means that for you example all that needs to be done with the unapply method is the third option, which returns a tuple of values. Following that, unlike the case class which automatically creates immutable fields for you, you'll need to add the val annotation to you class declaration.
class abc (val age: Int, val name: String)
And:
def unapply(obj: abc): Option[(Int, String)] = Some((obj.age, obj.name))