Scala subtypes generics
I am lost in scala generics.
I need a method storeUnit
that takes Unit
instances of subclasses (eq Visit
) and returns StoredUnit
instances of subclasses (eq StoredVisit
), but I am getting compilation errors.
trait StatsUnit { val ip: String }
case class Visit(ip: String) extends StatsUnit
case class Click(ip: String) extends StatsUnit
trait StoredStatsUnit extends StatsUnit { val id: String }
case class StoredVisit(id: String, ip: String) extends StoredStatsUnit
case class StoredClick(id: String, ip: String) extends StoredStatsUnit
def storeUnit[A <: StatsUnit, B <: StoredStatsUnit](statsUnit: A): B = {
statsUnit match {
case x: Visit => StoredVisit("myid", x.ip)
case x: Click => StoredClick("myid", x.ip)
}
}
/tmp/1.scala:11: error: type mismatch;
found : this.StoredVisit
required: B
case x: Visit => StoredVisit("myid", x.ip)
^
/tmp/1.scala:12: error: type mismatch;
found : this.StoredClick
required: B
case x: Click => StoredClick("myid", x.ip)
source to share
Comment first:
- Don't name your trait
Unit
!Unit
has a definite meaning in Scala - it is equivalent to Javavoid
- and shading this definition will only cause problems!
However, the problem is when you specify that your method will return an instance B
and then you try to return something like StoredVisit
. You don't need it B
at all in this example, so the following will work fine:
def storeUnit[A <: StatsUnit](unit: A): StoredStatsUnit = {
StoredVisit("myid", unit.ip)
}
source to share
Let it lie to the compiler to compile the code and then I'll show you what the compiler is complaining about. Firstly:
scala> def storeUnit[A <: StatsUnit, B <: StoredStatsUnit](unit: A): B = {
| StoredVisit("myid", unit.ip).asInstanceOf[B]
| }
storeUnit: [A <: StatsUnit, B <: StoredStatsUnit](unit: A)B
Now let's create another subclass StoredStatsUnit
:
case class UnStoredVisit(id: String, ip: String, n: Int) extends StoredStatsUnit
And now show why the compiler complained about the definition of this method:
scala> val visit: UnStoredVisit = storeUnit(Visit("1.2.3.4"))
java.lang.ClassCastException: StoredVisit cannot be cast to UnStoredVisit
In other words, you are not returning a parameterized B that is an arbitrary subclass StoredStatsUnit
. You return StoredVisit
which is one of its subclasses.
source to share