先看一段 java代码
public class test { public static void main(String[] args) throws ClassNotFoundException, SQLException { List<Pet> list = new ArrayList<Pet>(); list.add(new Dog("Limbo",1)); list.add(new Dog("Yu",1)); workWithPets(list); } public static void workWithPets(List<Pet> list) { for(Pet p : list) { System.out.println(p); } } } class Pet{ private String name; private int age; public Pet(){} public Pet(String name,int age) { this.name = name; this.age = age; // TODO Auto-generated constructor stub } @Override public String toString() { return "name: "+ this.name +" age: "+ this.age; } } class Dog extends Pet{ public Dog(String name,int age) { super(name,age); // TODO Auto-generated constructor stub } }OK,这一段代码编译执行均无问题
接下来让我们看一段Scala代码
class Pets(var name:String,var age:Int) { override def toString() :String = {"name: " + name + " age: " + age} } class Dog(override var name:String,override var age:Int )extends Pets(name, age) {} object test3{ def main(args: Array[String]): Unit = { val dogs = Array(new Dog("Limbo",1),new Dog("Yu",2)) workWithPets(dogs) //Compiling Error } def workWithPets(pets:Array[Pets])={ pets.foreach(println) } }调用workWithPets()的时候,Scala会提示错误,Dog数组不能穿给接收Pet数组的方法,但是,对这个方法而言,这么做没有什么不良后果。不过Scala不知道这一点,它试图保护我们。我们需要做的就是告诉Scala这样做是可行的
def workWithPets[T <: Pet](pets:Array[T])={ pets.foreach(println) }这里用特殊的语法定义了workWithPets()。 T <: Pet 表示T所代表的类派生自Pet。通过使用这种上界语法,我们告诉Scala,具有参数化类型T的类型T的参数数组必须至少是Pet的数组,也可以是Pet的派生类的数组。
协变:将子类实例的容器赋给基类容器的能力
逆变:将超类实例的容器赋给子类容器的能力
例子:
如果一个方法要接受Dog参数,那么另一个接受Animal参数的方法肯定也可以接受这个方法的参数,这是Animal向Dog方向的转变是逆变。如果一个方法要求的返回值是Animal,那么返回Dog的方法肯定是可以满足其返回值要求的,这是Dog向Animal方向的转变是协变。