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)

      

+3


source to share


3 answers


Comment first:

  • Don't name your trait Unit

    ! Unit

    has a definite meaning in Scala - it is equivalent to Java void

    - 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)
}

      

+3


source


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.

+3


source


B

is a subtype StoredUnit

and StoredVisit

is a subtype StoredUnit

, but there is no valid inference as to what is StoredVisit

compatible with B

.

+1


source







All Articles