scala-5(协变和逆变)

scala-5


scala中的协变和逆变,作用类似于java的泛型上下界限
定义三个类Animal、Bird和Sparrow,关系图如下:

继承
继承
Sparrow
Bird
Animal

定义一个带泛型的类

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位置就不报错了,此时的类型关系

继承
Home[Bird]
Home[Animal]

如果想要home1不报错,转换逻辑就与多态赋值相反,这就是逆变逻辑

class Home[-T]

修改完成后,home1就不会报错了,同时home3又开始报错
此时的类型关系

继承
Home[Animal]
Home[Bird]

总结

  • 当类标记为[+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三种类型的对象都无法返回

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值