5

我试图理解为什么 Scala 编译器不能推断出对路径相关类型的以下限制:

trait MyTrait
class MyTraitImpl extends MyTrait
trait MyTrait2[A <: MyTrait] {
  type MyTraitType = A
}
class MyTrait2Impl[A <: MyTrait] extends MyTrait2[A]

val obj: MyTrait2[_] = new MyTrait2Impl[MyTraitImpl]

def myMethod[A <: MyTrait](t2: MyTrait2[A]) = println("Hi!")

myMethod[obj.MyTraitType](obj)
// <console>:14: error: type arguments [obj.MyTraitType] do not conform to method myMethod's type parameter bounds [A <: MyTrait]
//               myMethod[obj.MyTraitType](obj)

对我来说,直觉上,MyTraitType不能是 a 的子类MyTrait,因为边界就在Ain上MyTrait2。如果有,你能给我一个例子或指出这个代码片段的错误吗?

如果这是 Scala 编译器的限制,谁能告诉我一种使用类型系统实现这一目标的方法?注意:

  • 我没有MyTrait对象,也没有myMethod收到;
  • 我不需要myMethod知道具体的类型A;它只需要知道A它是 的子类型MyTrait并且t2参数化了A;
  • 中的下划线obj是有意的;在我打电话myMethod的地方,我不知道具体类型A(否则不会有问题);
  • 我更喜欢不需要修改的解决方案myMethod
4

2 回答 2

3

您应该只在类型成员上使用约束,而不是在MyTrait2声明中对类型参数使用边界:

trait MyTrait
class MyTraitImpl extends MyTrait
trait MyTrait2 { // Remove [A <: MyTrait]
  type MyTraitType <: MyTrait // add <: MyTrait
}
class MyTrait2Impl[A <: MyTrait] extends MyTrait2 { type MyTraitType = A }

val obj: MyTrait2 = new MyTrait2Impl[MyTraitImpl]

def myMethod[A <: MyTrait](t2: MyTrait2{ type MyTraitType = A }) = println("Hi!")

myMethod[obj.MyTraitType](obj)

正如预期的那样,您将收到错误类型的编译错误:

scala> val otherObj: MyTrait2 = new MyTrait2Impl[MyTraitImpl]
otherObj: MyTrait2 = MyTrait2Impl@8afcd0c

scala> myMethod[obj.MyTraitType](otherObj)
<console>:15: error: type mismatch;
 found   : otherObj.type (with underlying type MyTrait2)
 required: MyTrait2{type MyTraitType = obj.MyTraitType}
              myMethod[obj.MyTraitType](otherObj)
                                        ^

证明它适用于List[MyTrait2]

scala> for {
     |   obj <- List[MyTrait2](
     |            new MyTrait2Impl[MyTraitImpl],
     |            new MyTrait2Impl[MyTraitImpl]
     |          )
     | } myMethod[obj.MyTraitType](obj)
Hi!
Hi!
于 2013-08-07T11:56:50.980 回答
1

如果您真的想保留泛型参数(我同意,在这种情况下抽象类型会更好),您可以执行以下操作:

限制 的通配符类型obj

val obj: MyTrait2[_ <: MyTrait] = new MyTrait2Impl[MyTraitImpl]

不带类型参数的调用myMethod并让编译器弄清楚:

myMethod(obj)

这无非是帮助编译器进行类型推断。你的推理MyTrait2#MyTraitType当然是正确的。

另一个解决方案(进入同一类别)是删除类型obj

val obj = new MyTrait2Impl[MyTraitImpl]

但是,当然,这可能不适用于您的实际用例。

于 2013-08-07T19:25:27.573 回答