Reputation: 3350
I am learning scala and tried following form Scala Cookbook:
trait Animal
trait FurryAnimal extends Animal
case class Dog(name:String) extends Animal
case class Cat(name:String) extends Animal
Now when I did following as :
val x = Array(Dog("Fido"),Cat("Felix"))
it show result as :
x:Array[Product with Serializable with Animal] = Array(Dog(Fido),Cat(Felix))
Although I know that a case class is mixed in with Product trait
What I am not getting is : Product with Serializable with Animal
As per my understanding Product has something to do with Pattern matching
I did google it but didn't get anything.Please Help to get me the concept in detail.
Thanks
Upvotes: 40
Views: 36277
Reputation: 149548
All case classes in Scala posses a few properties:
Product
trait and a default implementation will be provided for them, as they can be viewed as a Cartesian Product of N records.Serializable
as they are serializable out of the box (as a design choice).hashCode
and equals
provided by the compiler, which aids with pattern matchingapply
and unapply
methods, for composition and decomposition of the type.Case classes are also Scala's way of expressing an Algebraic Data Type, more specifically a Product Type. Tuples are also a product type, and as so they also extend the Product
trait.
When you use two case classes with a common trait, the scala's compiler will use it's type inference algorithm to attempt to find the least upper bound (LUB) for the T
type of the Array
. Since both case classes extend Product
and Serializable
through the compiler, and the trait
does not, it will during the search and apply it to the final calculated type.
If you want to avoid seeing that, you can have your trait explicitly extend those traits:
sealed trait Animal extends Product with Serializable
More on this topic can be found here
Upvotes: 15
Reputation: 5206
This is an expected behavior because of how case class
works. case class
automatically extends
two traits, namely Product
and Serializable
.
Product
trait is extended as case class
is an algebraic data type with product type.
Serializable
trait is extended so that case class
can be treated as a pure data - i.e capable of being serialized.
Unlike case class
Dog
and Cat
, your trait Animal
does not extend Product
or Serializable
. Hence the type signature you see.
When you declare something like Array(Dog(""), Cat(""))
, scalac needs to infer single top type that can represent all the elements of given array.
That's why the inferred type is Product with Serializable with Animal
as Animal
did not extend Product
nor Serializable
while the case class
did implicitly.
To work around this inference, you can either make type explicit by Animal
or make Animal
extend Product
and Serializable
.
trait Animal extends Product with Serializable
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
Array(Dog(""), Cat("")) // Array[Animal] = Array(Dog(), Cat())
Upvotes: 66
Reputation: 4112
All case classes automatically extend Product
and Serializable
. It looks ugly ? yes.
Basically ,Product
can be viewed as heterogeneous collections. All Product classes viz. (Product1 , Product2 ...) extends Product
which contains some common methods to use like productArity
, productElement
etc.
Like Case classes other types that extends Product
are List
,Tuple
etc
From my scala worksheet,
val product : Product = (10,"String",3) //> product : Product = (10,String,3)
product.productArity //> res0: Int = 3
product.productElement(0) //> res1: Any = 10
product.productElement(1) //> res2: Any = String
product.productElement(2) //> res3: Any = 3
case class ProductCase(age:Int,name:String,ISBN:Int)
ProductCase(23,"som",5465473).productArity //> res4: Int = 3
For details look here.
Upvotes: 0