考虑以下两个表达式之间的区别:
Enum.valueOf(x.asInstanceOf[Class[X] forSome { type X <: Enum[X] }], name)
和:
Enum.valueOf(x.asInstanceOf[Class[X forSome { type X <: Enum[X] }]], name)
现在考虑在每种情况下如何推断T
类型参数。valueOf
在第一种情况下,我们有一个X
我们知道是 的子类型的Enum[X]
,我们都准备好了。另一方面,在第二种情况下,T
必须是X forSome { type X <: Enum[X] }
,而且至关重要的是,这种类型不是 的子类型Enum[X forSome { type X <: Enum[X] }]
,所以我们没有满足 上的约束T
。
问题是您的第二个示例等效于后者。
Enum
作为一个脚注,如果它的类型参数是协变的,这将工作得很好。采取以下简化示例:
trait Foo[A]
trait Bar[A]
def foo[A <: Bar[A]](f: Foo[A]) = f
def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???
现在foo(x)
将编译,但foo(y)
不会,就像在您的代码中一样。但是稍微改变Bar
一下:
trait Foo[A]
trait Bar[+A]
def foo[A <: Bar[A]](f: Foo[A]) = f
def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???
现在它们都将编译。我猜这与我们对您的两个示例等效的直觉如此强烈的原因有关。
作为另一个脚注(响应gzmo下面的评论),请考虑以下内容:
scala> trait Foo[A <: Foo[A]]
defined trait Foo
scala> class MyFoo extends Foo[MyFoo]
defined class MyFoo
scala> val myFoo = new MyFoo
myFoo: MyFoo = MyFoo@3ee536d
scala> myFoo: (X forSome { type X <: Foo[X] })
res0: X forSome { type X <: Foo[X] } = MyFoo@3ee536d
scala> myFoo: Foo[MyFoo]
res1: Foo[MyFoo] = MyFoo@3ee536d
让我们假设这X forSome { type X <: Foo[X] }
是一个子类型Foo[X forSome { type X <: Foo[X] }]
(暂时忽略后者甚至不是有效类型的事实)。然后我们就可以写出以下内容:
myFoo: Foo[X forSome { type X <: Foo[X] }]
但是Foo
是不变的,所以如果我们有一些东西是 和 的实例Foo[A]
,Foo[B]
那么它一定是A =:= B
。但绝对不是这样的MyFoo =:= (X forSome { type X <: Foo[X] })
。不确定所有这些是否会减少混乱,但这就是我如何说服自己编译器知道它在这里做什么的方式。