协变类型T出现在逆变位置。

20

我知道这个问题以前已经被问过了,但是要么答案不适用于这种情况,要么我不理解它们。

基本上,为什么下面这个(重新创建我的问题的简单示例)不起作用?

class Test[+T] {
    var list: List[T] = _
}

我遇到的问题是,我有一个对象,想要传入一个 Test[Nothing] 实例(空的 Test),但是除非我将 T 中的 Test 协变,否则无法实现此操作。
1个回答

27
T 中的测试协变意味着对于任何 ATest[A] 都是 Test[Any] 的子类型。因此让我们创建一个 Test
val test_string = new Test[String]

现在我们有一个Test[String],其中包含的list类型是List[String]
由于Test[String]Test[Any]的子类型,因此应该允许以下操作:
val test_any : Test[Any] = test_string

现在,我们有一个Test[Any],因此test_any.list是类型List[Any],这意味着以下内容应该是有效的:

test_any.list = List[Any]()

这意味着我们刚刚将一个 List[Any] 分配给了 test_strings 列表成员,这是不允许的,因为它应该是一个 List[String],而不是一个 List[Any]。这也意味着你可以在列表中添加任何内容,因为它是类型 List[Any]

1
那么我如何让包含列表随着Test类的类型参数而变化,还是这不可能? - sksamuel
1
@monkjack 你可以将 list 定义为 valclass Test[+T](val list: List[T]),但这取决于何时初始化列表,可能并不总是可行的选择。 - Malte Schwerhoff
是的,我注意到这个可以工作,但不幸的是我正在使用json,并且据我所知Jackson不能解组到一个类构造函数。我可能会看看是否可以在我的用途中使用一个case类。 - sksamuel
11
在这里具体的问题是将list定义为var时,编译器会自动提供改变方法def list_=(l: List[T]): Unit。在这个方法中,T类型参数不在协变位置。如果你尝试将T设为逆变,由于相应的访问方法,情况也会发生类似的情况。除了将T设置为非变或使用一个val来定义你的列表之外,没有其他解决办法。 - pagoda_5b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接