Reputation: 11830
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)
Upvotes: 3
Views: 8662
Reputation: 297275
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.
Upvotes: 3
Reputation: 5069
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)
}
Upvotes: 3
Reputation: 26486
B
is a subtype of StoredUnit
and StoredVisit
is a subtype of StoredUnit
but there is no valid inference that StoredVisit
is compatible with B
.
Upvotes: 1