【问题标题】:Collections of Traits with types generics in Scala 2.10Scala 2.10 中具有泛型类型的特征集合
【发布时间】:2015-06-18 23:25:07
【问题描述】:

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

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)回不正确的类型,但我似乎无法让它工作。提前致谢!

【问题讨论】:

    标签: scala generics reflection scala-2.10


    【解决方案1】:

    我不确定你想做什么,但你面临的问题是因为你的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
    

    【讨论】:

    • 感谢 marios,但问题是我需要硬编码我想在模式匹配代码中执行对象的操作。我想基本上在运行时从列表中检索一个对象并将其转换为适当的类(所以,FooBar),这样我就可以在它上面执行其他任意代码(比如Foo.f() 或其他方法)。我不想有一个专门用于执行f() 的模式匹配,一个用于f2()MyTrait 中存在的任何其他方法,我认为这就是conditionalDo 解决方案所指向的。我错过了什么吗?
    • 集合围绕单一类型进行抽象(例如,List[Int])。如果您有一个具有不同类型的集合,那么更通用的分母需要是您的类的类型。在您的情况下,是List[MyTrait[_]]。如果你需要一个能记住每个元素类型的集合,那么 Scala 有 Tubles 来做到这一点。 val myTraitCollection = (fooInstance, barInstance)。然后,您可以将其作为 myTraitCollection._1 和 myTraitCollection._2 访问,并保留所有类型信息。如果这是您想要做的,我可以将其添加到答案中。
    • 是的,确切地说,我想要一个具有不同类型但共享Trait 的集合。例如,任何类型的MyTraits 中的List。当我尝试检索此List 的元素并将其转换回其原始(子)类时,问题就出现了,例如FooBarTuples 对我不起作用,因为它们的数量必须在编译时知道。 HLists 来自 shapeless 更灵活,但您仍然无法在运行时确定任意数量的它们,因此我依赖某种集合...再次感谢您的帮助!
    • 我确信有更好的方法来满足您的需求。如果您知道位置 1 的元素是 Foo,而位置 2 的元素是 Bar,那么为什么不使用 Tuble?如果你想要一个任意长的东西列表,你怎么知道位置(比如)234 上的元素是 Foo() 类型的?您要么使用模式匹配,要么拥有一个具有相同输入类型的基方法,每个超类以不同的方式实现。
    • 感谢您一直以来的帮助,马里奥斯。如果我在编译时知道 arity,元组将是一个很好的解决方案。我想根据该输入解析用户输入并动态创建对象列表(长度仅在运行时已知)。您的第二个解决方案更接近,但省略了类型泛型(我怀疑这可能与这个问题的难度有关)。我不禁觉得我正在尝试做的是基本的面向对象:我有一个父类的子类,我想将它们一起存储在一个列表中,并通过反射识别它们的类。事实上,getClass 工作正常,但不是 f
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多