score:0

Accepted answer

I ended up implementing this using type classes. No loss of type information, and no need for type casing (or type casting).

The only weird bit I can see is choosing to pimp retrieve on to the Bar and Baz classes directly, but it seems to do the trick.

trait Foo
case class Bar(x: String) extends Foo
case class Baz(x: String) extends Foo

trait Retrievable[A] {
  def retrieve: List[A]
}

val _bars: List[Bar] = List(Bar("bar a"),Bar("bar b"))
val _bazs: List[Baz] = List(Baz("baz 1"),Baz("baz 2"))

implicit def barsRetrievable(x: Class[Bar]): Retrievable[Bar] =
  new Retrievable[Bar] {
    override def retrieve: List[Bar] = _bars
  }

implicit def foosRetrievable(x: Class[Baz]): Retrievable[Baz] =
  new Retrievable[Baz] {
    override def retrieve: List[Baz] = _bazs
  }

val bars: List[Bar] = classOf[Bar].retrieve
// bars: List(Bar(bar a), Bar(bar b))

val bazs: List[Baz] = classOf[Baz].retrieve
// bazs: List(Baz(baz 1), Baz(baz 2))

score:0

One way to eliminate the cast is by using an existential type:

trait Foo
trait Bar extends Foo
trait Baz extends Foo                                       

case object Bar1 extends Bar                                
case object Baz1 extends Baz                                

def retrieve[F <: Foo](implicit m: Manifest[F]): List[_ <: Foo] = m.erasure match {
  case x if x.isAssignableFrom(classOf[Bar]) => List[Bar]()  
  case x if x.isAssignableFrom(classOf[Baz]) => Nil  
  case _ => Nil
}           

Unfortunately there still seems to be some loss of type information, since it isn't known at compile-time which Foo implementation will be returned:

val foos: List[Foo] = retrieve[Bar]                         
// val bars: List[Bar] = retrieve[Bar] // does not compile

Related Query

More Query from same tag