18.6 协变,逆变和不变

参考文章: https://www.originate.com/thinking/stories/cheat-codes-for-contravariance-and-covariance/

Scala 阻止我们将一个指向 ArrayList[Int]的引用赋值给一个指向 Array List[Any]的引用。这是一件好事情。

通常来说,一个派生类型的集合不应该赋值给一个基类型的集合。

如果在期望接收一个基类实例的集合的地方,能够使用一个子类实例的集合的能力叫作协变(covariance)

如果而在期望接收一个子类实例的集合的地方,能够使用一个超类实例的集合的能力叫作逆变(contravariance)

在默认的情况下,Scala 都不允许, 即不变(invariance)。

18.6.1 定制集合的型变

我们也可以在定义集合类的时候控制这一行为,让集合类直接支持协变和逆变.

使用[+T]支持协变, 使用[-T] 支持逆变. [T]表示不变.

package day02.po

// Pet 类
class Pet(val name: String) {
    override def toString: String = name
}

// Dog类, 并继承 Pet类
class Dog(override val name: String) extends Pet(name)

// 定义支持协变的类
class MyList1[+T] {}

//  定义支持逆变的类
class MyList2[-T] {}

object Test {

    def main(args: Array[String]): Unit = {
        var pets1: MyList1[Pet] = new MyList1[Pet]
        var dogs1: MyList1[Dog] = new MyList1[Dog]
        pets1 = dogs1 // 编译正确  协变

        //------
        var pets2: MyList2[Pet] = new MyList2[Pet]
        var dogs2: MyList2[Dog] = new MyList2[Dog]
        dogs2 = pets2 // 编译正确  逆变
    }
}

在默认情况下,Scala 编译器将会严格检查型变。

我们也可以要求对协变或者逆变进行宽大处理。无论如何,Scala 都会检查型变是否可靠。


18.6.2 协变点和逆变点

对方法来说参数类型必须是逆变的, 而返回值则必须是协变的.

所以, 如果一个类型即要出现在参数位置, 又要出现的返回值位置, 则应该使用不便

class Printer[+A] {
    // 编译失败: covariant type A occurs in contravariant position in type A of value n
    def print(n: A): Unit = {
        println(n)
    }

    // 使用下界来解决
    def print[U >: A](n: U): Unit = {
        println(n)
    }

}

class Printer[-A] {
    // 编译失败: contravariant type A occurs in covariant position in type (n: A)A of method print
    def print(n: A): A = {
        println(n)
        n
    }
}

注意: 如果参数是函数, 则做为参数的函数的型变是反过来的.

Copyright © 尚硅谷大数据 2019 all right reserved,powered by Gitbook
该文件最后修订时间: 2019-04-21 10:28:58

results matching ""

    No results matching ""