I am lost in scala generics.
I need a method storeUnit which accepts Unit's subclasses's instances (e.q. Visit) and returns StoredUnit's subclasses's instances (e.q. 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)
A comment first:
Unit! Unit has specific meaning in Scala - it's equivalent to Java's void - and shadowing that definition is only going to cause troubles!However, the problem here is that you specify that your method will return an instance of B and then you try to return something of type StoredVisit. You don't need B at all in this example, so the following will work fine:
def storeUnit[A <: StatsUnit](unit: A): StoredStatsUnit = {
StoredVisit("myid", unit.ip)
}
Let's lie to the compiler to make the code compile, and then I'll show what the compiler is complaining about. First:
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 of StoredStatsUnit:
case class UnStoredVisit(id: String, ip: String, n: Int) extends StoredStatsUnit
And now let's show why the compiler was complaining about that method definition:
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 which is an arbitrary subclass of StoredStatsUnit. You are returning a StoredVisit, which is one specific subclass of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With