scala-5
scala中的协变和逆变,作用类似于java的泛型上下界限
定义三个类Animal、Bird和Sparrow,关系图如下:
定义一个带泛型的类
class Home[T]
class Animal
class Bird extends Animal
class Sparrow extends Bird
def main(args: Array[String]): Unit = {
val home1:Home[Bird]=new Home[Animal] // 编译报错
val home2:Home[Bird]=new Home[Bird] // 编译正常
val home3:Home[Bird]=new Home[Sparrow] // 编译报错
}
在scala中,泛型不同的类就属于不同的类型了,如果要限定Bird的子类可以传入类中,就需要类似于多态赋值的转换,这个就是协变
表示形式为[+T]
,修改Home类代码
class Home[+T]
修改完成后可以看到,home3位置就不报错了,此时的类型关系
如果想要home1不报错,转换逻辑就与多态赋值相反,这就是逆变逻辑
class Home[-T]
修改完成后,home1就不会报错了,同时home3又开始报错
此时的类型关系
总结
- 当类标记为[+T]时,相当于限定了类型的上限,只能传入指定类和该类的子类(直接或间接)
- 当类标记为[-T]时,相当于限定了类型的下限,只能传入指定类和该类的父类(直接或间接)
[+T]/[-T]这种写法适用于类的标记,如果要在方法中传入的参数要限定上下界限,使用>:
和<:
指定
例如:
def main(args: Array[String]): Unit = {
// 这里限定了传入类型的上限
def printClassInfo[T <: Bird](t: T): Unit = {
println(t.getClass.getName)
}
//printClassInfo(new Animal)
printClassInfo(new Bird)
printClassInfo(new Sparrow)
}
可以看到当传入一个Animal对象时,运行会报错
因为这里限定了传入类型必须为Bird类或Bird类的子类,所以传入Animal会报错
相反,我们定义一个限定下限的函数用于返回对象
def main(args: Array[String]): Unit = {
// 这里限定了输出类型的上限
def getClassType[T >: Bird](): T = {
//new Animal
new Bird
new Sparrow
}
println(getClassType())
}
依次测试方法中返回Animal、Bird、Sparrow三种类型的对象,结果为返回Animal对象会(编译)报错,其他两种类型的对象可以正常返回
这里比较容易理解,我们限定了返回值类型为Bird,返回Bird自然可以正常返回,返回Sparrow时是将Sparrow对象作为Bird对象返回,这符合多态原则,因此也可以正常返回,而当返回Animal对象时,Animal对象无法视作Bird对象,就会报错。
接下来,尝试下反转函数中限定的上下界限,即传入参数的函数限定类型的下限,输出对象的函数限定类型的上限
def main(args: Array[String]): Unit = {
def printClassInfo[T >: Bird](t: T): Unit = {
println(t.getClass.getName)
}
printClassInfo(new Animal)
printClassInfo(new Bird)
printClassInfo(new Sparrow)
}
先看传入参数时限定类型下限,这里参数是要接收外来对象的,所以相当于多态表达式左侧的变量,而多态规则中左侧变量需要是右侧类型或其类型的父类,但此时左侧变量类型未确定上限,因此无法对右侧传入参数类型作出限制,运行可以看到,三个对象都正常传入了方法,并输出了结果
为了验证结论,再传入一些其它类型的数据
printClassInfo("123")
printClassInfo(12)
printClassInfo(12.0)
printClassInfo(new util.ArrayList[Int]())
printClassInfo(new Object)
可以看到运行结果:
所有类型都输出了结果,类型限制也就失去了意义
def getClassType[T <: Bird](): T = {
new Animal
new Bird
new Sparrow
}
再看当返回对象时限定类型上限,这里返回值类型明确为Bird类或Bird类的父类,虽然我们可以明确看到Bird类和Sparrow类都是满足要求的,但实际编译过程中,编译器无法明确确认这点(这个特性就和Java确定下限的泛型通配符一样),所以此时Animal、Bird和Sparrow三种类型的对象都无法返回