以下内容是我在看了hongjiang
的博文之后的一些体会,做个记录,感谢hongjiang
。
先给一个示例,定义一个类A
,其类型参数是协变的:
class A[+T] {
def func(x: T) {}
}
上面的代码通不过编译,报错如下:
covariant type T occurs in contravariant position in type T of value x
要解释这个问题,需要理解协变点和逆变点的概念。我们可以考虑这样一种情况来解释程序为什么报错,既然A
的类型参数T是协变的,那么A[AnyRef]
是A[String]
的父类,A[AnyRef]
对应的func
为func(AnyRef)
,A[String]
对应的func
为func(String)
,我们定义father
是一个A[AnyRef]
实例,child
是A[String]
实例。当我定义了另一个函数g
,g
的参数为A[AnyRef]
,因此g(father)
当然是没有问题的,又因为child
是father
的子类,因此按理来说g(child)
也是没有问题的,但是father
的func
可以接受AnyRef
类型的参数,而child
的func
只能接受String
类型的参数。因此,如果编译器允许用child
替换father
,那么替换后g
中的参数调用func
就只能传入String
类型的参数了,相当于g
的处理范围缩小了。所以编译器不允许这种情况,因此会报错。反过来一想,如果传入的是father
的父类,那么g
的处理范围就变大了,所有适用于father
的情况都适用于father
的父类,因此,如果把A的类型参数T
声明为逆变的,就不会有问题了。
class A[-T] {
def func(x: T) {}
}
总结:传入A
的类型参数会作为A
中方法的参数的类型(如果有参数的话),我们知道一个方法中如果有类型为X
的参数,那么这个方法可以接受类型为X
的子类的参数。同理上面的情况,func
原来可以接受类型为AnyRef
及AnyRef
的子类作为参数,但是如果一协变,那么func
就只能接受类型为String
及String
的子类作为参数,作用范围减小了。
因此方法的参数的位置被称为逆变点,A
中的类型参数声明为协变,因此编译时出错,声明为协变则没有问题。
而方法返回值的位置被称为协变点,同样考虑上面的情况:
class A[+T] {
def func(): T = {
null.asInstanceOf[T]
}
}
也是考虑father:A[AnyRef]
和child:A[String]
,当用child
替换father
后,child
的func
方法会返回String
类型的对象来替换AnyRef
,因此是合理的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。