2014-10-18 87 views
4

下面的代码不会(在斯卡拉2.11)编译:我怎样才能别名协泛型类型参数

case class CovariantClass[+R](value: R) { 
    type T = R 
    def get: R = value 
} 

object Main { 
    def main(args: Array[String]): Unit ={ 
    println(CovariantClass[String]("hello").get) 
    } 
} 

的错误信息是:

Error:(4, 8) covariant type R occurs in invariant position in type R of type T 
    type T = R 
    ^

为什么我不能别名协变类型参数?如果我删除行type T = R,代码编译并打印hello,所以别名似乎是问题。不幸的是,这意味着我不能为更复杂的类型创建别名,例如type T = List[R]也不能编译,尽管List是协变的。

回答

4

scala spec

一个类型别名的右手侧始终处于不变的位置。

这意味着你不能创建别名T并在右侧指定变量类型R。这同样适用于List[R],因为它也是协变的。

可以,但是提供了类型参数类型别名:

case class CovariantClass[+R](value: R) { 
    type T[+R] = List[R] 
    def get: R = value 
} 

如果你发现自己想别名类型参数R,你应该刚刚命名摆在首位别的东西。

+0

谢谢你的答案和引用scala规范。我同意重命名'R'而不是别名,但我喜欢为''[ReturnValueInCaseOfFailure,R]''做一个别名。不幸的是,你提出的解决方法使'T'参数化,但它可能是我能达到我想要的最接近的。 – 2014-10-18 11:18:20

3

这是被禁止的,因为它会允许一个不正确的程序,这总是规则。你可以把它改写这样的:

case class CovariantClass[+R](value: R) { 
    type T <: R 
    def get: R = value 
} 

至于它是如何把一个例子,可以这样考虑:

case class CovariantClass[+R](value: R) { 
    type T = Int 
    def get: R = value 
    def put(x: T) {} 
    def put2(x: R) {} 
} 

由于T是如何定义的,它是不变的。这意味着它可以用于协变类型不能的地方,例如上面所见。请注意0​​编译但put2没有。

+0

有趣。尽管在这个例子中,“R”应该是“T”的下界,代码打破了吧?另外,我不明白如何在不引入类型绑定的情况下打破它。这只是一个保守的设计选择吗?无论如何:谢谢你的例子。 – 2014-10-18 11:11:35

+0

@KuluLimpa我为可能出现的问题添加了一个示例。这只是一个例子的开始,以显示这如何与差异问题相关。如果您需要更多信息,请查看有关差异的问题 - 应该有些例子说明为什么某些职位的变体类型是非法的(我知道我以前写过一些)。 – 2014-10-25 00:45:09