Scala 2.10中具有类型泛型的特征集合

时间:2015-06-18 23:25:08

标签: scala generics reflection scala-2.10

我试图通过引用他们的超类(好,特性)来构建在运行时定义并使用类型泛型的对象集合,但是我很难将它们转换回到儿童对象。一些示例代码和结果:

trait MyTrait[T] {
  def f(x: T): Int
}

class Foo extends MyTrait[Double] {
  def f(x: Double) = 1
}

class Bar extends MyTrait[String] {
  def f(x: String) = 2
}

val fooInstance: Foo = new Foo
val barInstance: Bar = new Bar
val myTraitList: List[MyTrait[_]] = List(fooInstance, barInstance)

println(fooInstance.getClass)
// prints "class MyExample$Foo"
println(barInstance.getClass)
// prints "class MyExample$Bar"

println(myTraitList(0).getClass)
// prints "class MyExample$Foo", so the list is preserving object classes and not just "MyTrait[_]"
println(myTraitList(1).getClass)
// prints "class MyExample$Bar", again, preserving object class

println(fooInstance.f(1.0))
// prints "1"
println(barInstance.f("blah"))
// prints "2"

println(myTraitList(0).f(1.0))
// this is where things break:
// "type mismatch; found : Double(1.0) required: _$3 where type _$3"
// so, the list element knows it's an instance of Foo (as indicated by getClass), but has lost the overloaded definition of f

println(myTraitList(1).f("blah"))
// the same error occurs on the Bar instance:
// "type mismatch; found : String("blah") required: _$3 where type _$3"

如果我对myTraitList(0).asInstanceOf[Foo].f(1.0)进行硬编码,它(可预测)就可以正常工作。所以,我尝试在运行时创建一个函数来执行此操作:

def castToCorrectChildClass(o: MyTrait[_], label: Char): MyTrait[_] = {
  return label match {
    case 'f' => o.asInstanceOf[Foo]
    case 'b' => o.asInstanceOf[Bar]
    case _ => o
  }
}

不幸的是,这也存在同样的问题:

println(castToCorrectChildClass(myTraitList(0), 'f').f(1.0))
// type mismatch; found : Double(1.0) required: _$2 where type _$2

一种解决方案是使List[MyTrait[Any]]存储实例并使类型参数协变:trait MyTrait[+T]。不幸的是,在我的真实世界代码中,我需要它因其他原因而保持不变,因此我无法使用此解决方法。我也尝试过使用ClassTags和TypeTags来记住孩子们认为这是一个与反思有关的问题,但我没有运气(我怀疑那不是预期的用例)对于这些,无论如何,虽然也许我错了。)

有什么建议吗?我喜欢灵活收集未知号码(因此,没有Tuples)的子对象,这些子对象扩展了相同的特性,但根据用户输入在运行时收集了不同的类型,我很喜欢很高兴接受做记账的权衡,以确保我不会错误地(或错误 - asInstanceOf)一个对象回到错误的类型,但我似乎无法得到它工作。提前谢谢!

1 个答案:

答案 0 :(得分:0)

我不确定您要做什么,但您遇到的问题是因为myTraitList的类型与List[MyTrait[Any]]类似。由于类型为_,编译器不知道它是Bar还是Foo。值得庆幸的是,Scala提供了模式匹配,允许您对对象的类型进行条件。

解决方案1 ​​

def conditionalDo(x: MyTrait[_]) = {
    x match {
        case i: Foo => println("doing foo() stuff")
        case j: Bar => println("doing bar() stuff")
        case _ => println("error!")
    }
}

myTraitList.foreach(conditionalDo)

解决方案2

trait MyTrait {
  def f(x: Any): Int
}

class Foo extends MyTrait {
  def f(x: Any) = {
    x match {
       case i: Int => 1
       case _ => -1
    }
  }
}

class Bar extends MyTrait {
  def f(x: Any) = {
    x match {
       case j: String => 2
       case _ => -1
    }
  }
}

val fooInstance: Foo = new Foo
val barInstance: Bar = new Bar

val myTraitList: List[MyTrait] = List(fooInstance, barInstance)
myTraitList(0).f(1) // returns 1
myTraitList(1).f("s") // returns 2