Scala 通俗易懂 ---- 协变、逆变、不变
2019-11-22 本文已影响0人
数星星的鱼
协变、逆变、不变
Scala 语言中协变、逆变、不变是指拥有泛型的类型,在声明和赋值时的对应关系
协变:声明时泛型是父类,赋值时泛型可以是父类也可以是子类
逆变:声明时泛型是子类,赋值时泛型可以是子类也可以是父类
不变:声明时泛型是什么类型,赋值时也只能是什么类型
示例说明
准备工作
现有类声明如下:
// Dog 类继承自 Animal 类
class Animal
class Dog extends Animal
协变
声明语法:
// 协变类型,泛型前面有一个'加号'
class MyList1[+V]
示例代码:
val list1_1: MyList1[Animal] = new MyList1[Dog]() // 正确
val list1_2: MyList1[Animal] = new MyList1[Animal]() // 正确
val list1_3: MyList1[Dog] = new MyList1[Animal]() // 出错
说明:
协变类型声明时泛型是父类,赋值时泛型可以是父类,也可以是子类
逆变
声明语法:
// 逆变类型,泛型前面有一个'减号'
class MyList2[-V]
示例代码:
val list2_2: MyList2[Dog] = new MyList2[Dog]() // 正确
val list2_3: MyList2[Dog] = new MyList2[Animal]() // 正确
val list2_1: MyList2[Animal] = new MyList2[Dog]() // 出错
说明:
逆变类型声明时泛型是子类,赋值时泛型可以是子类,也可以是父类
不变
声明语法:
// 不变类型,泛型前面'没有符号'
class MyList3[V]
示例代码:
val list3_2: MyList3[Dog] = new MyList3[Dog]() // 正确
val list3_3: MyList3[Dog] = new MyList3[Animal]() // 出错
val list3_1: MyList3[Animal] = new MyList3[Dog]() // 出错
说明:
不变类型声明时泛型是子类,赋值时泛型只能是子类
完整代码
// Dog 类继承自 Animal 类
class Animal
class Dog extends Animal
// 声明拥有泛型的类,泛型前面有一个'加号'
class MyList1[+V]
// 声明拥有泛型的类,泛型前面有一个'减号'
class MyList2[-V]
// 声明拥有泛型的类,泛型前面'没有符号'
class MyList3[V]
def main(args: Array[String]): Unit = {
// 协变类型声明时泛型是父类,赋值时泛型可以是父类,也可以是子类
val list1_1: MyList1[Animal] = new MyList1[Dog]() // 正确
val list1_2: MyList1[Animal] = new MyList1[Animal]() // 正确
val list1_3: MyList1[Dog] = new MyList1[Animal]() // 出错
// 逆变类型声明时泛型是子类,赋值时泛型可以是子类,也可以是父类
val list2_2: MyList2[Dog] = new MyList2[Dog]() // 正确
val list2_3: MyList2[Dog] = new MyList2[Animal]() // 正确
val list2_1: MyList2[Animal] = new MyList2[Dog]() // 出错
// 不变类型声明时泛型是子类,赋值时泛型只能是子类
val list3_2: MyList3[Dog] = new MyList3[Dog]() // 正确
val list3_1: MyList3[Animal] = new MyList3[Dog]() // 出错
val list3_3: MyList3[Dog] = new MyList3[Animal]() // 出错
}
对比Java
Java 中只有不变,没有协变也没有不变,声明时泛型是什么类型,赋值时泛型也必须是什么类型
示例代码如下:
class Animal {}
class Dog extends Animal {}
static class MyList<X extends Animal> {}
public static void main(String[] args) {
MyList<Animal> m1 = new MyList<Animal>(); // 正确
MyList<Dog> m2 = new MyList<Dog>(); // 正确
MyList<Animal> m3 = new MyList<Dog>(); // 错误
MyList<Dog> m4 = new MyList<Animal>(); // 错误
}