最早遇到super和extends的时候没有注意他们的区别,因为也一直没用到过。最近在学习Kotlin的时候遇到了逆变、协变(in\out)的概念,决定好好研究一下。
是什么
先拿代码试一下
Kotlin版本
open class Person
open class User: Person()
open class Reader: User()
// Person > User > Reader
fun main() {
val readerList = mutableListOf<Reader>()
val userList = mutableListOf<User>()
val personList = mutableListOf<Person>()
addInUser(readerList) //报错
addInUser(userList)
addInUser(personList)
addOutUser(readerList)
addOutUser(userList)
addOutUser(personList) //报错
}
fun addInUser(readerList: MutableList<in User>) {
readerList.add(Person()) //报错 Type mismatch Required User Found Person
readerList.add(User())
readerList.add(Reader())
mutableListOf<Person>().addAll(readerList) // 报错 Type mismatch Required Collection<Person> Found MutableList<in User>
mutableListOf<User>().addAll(readerList) // 报错
mutableListOf<Reader>().addAll(readerList) // 报错
}
fun addOutUser(readerList: MutableList<out User>) {
readerList.add(Person()) //报错
readerList.add(User()) //报错
readerList.add(Reader()) //报错
mutableListOf<Person>().addAll(readerList)
mutableListOf<User>().addAll(readerList)
mutableListOf<Reader>().addAll(readerList) //报错
}
Java版本
public class Test {
public static class Person {}
public static class User extends Person {}
public static class Reader extends User {}
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
List<User> userList = new ArrayList<>();
List<Reader> readerList = new ArrayList<>();
addSuperUser(personList);
addSuperUser(userList);
addSuperUser(readerList); //报错
addExtendsUser(personList); //报错
addExtendsUser(userList);
addExtendsUser(readerList);
}
static void addSuperUser(List<? super User> list) {
list.add(new Person()); // 报错
list.add(new User());
list.add(new Reader());
new ArrayList<Person>().addAll(list); // 报错
new ArrayList<User>().addAll(list); // 报错
new ArrayList<Reader>().addAll(list); // 报错
}
static void addExtendsUser(List<? extends User> list) {
list.add(new Person()); // 报错
list.add(new User()); // 报错
list.add(new Reader()); // 报错
new ArrayList<Person>().addAll(list);
new ArrayList<User>().addAll(list);
new ArrayList<Reader>().addAll(list); // 报错
}
}
从代码是否报错可以看出super和in是类似的,extends和out是类似的。那么这两组关键字分别代表什么呢?
在main方法中,我们分别将三种list传给addInUser/addSuperUser、addOutUser/addExtendsUser
addInUser/addSuperUser只可以接收personList和userList
addOutUser/addExtendsUser只可以接收readerList和userList
也就是说
- 规定参数是in或super时,只能传这个类的List本身或者其父类的List;
- 规定参数是out或extends时,只能传这个类的List本身或者其子类的List。
为什么要这么规定?
来个高大上的:
Type covariance: This means that the container types have the same relationship to each other as the payload types do. This is expressed using the extends keyword.
Type contravariance: This means that the container types have the inverse relationship to each other as the payload types. This is expressed using the super keyword.
我看完也一头雾水。我粗暴的意译下就是covariance(协变)是说容器的类型是可以被荷载的参数的类型"搞"的。contravariance(逆变)是说容器的类型要"搞"荷载的参数类型的。
List<? extends User> 可以被所有User及其子类的容器add
List<? super User> 可以add所有User及其子类的容器
其实就是那句著名的
Producer Extends,Consumer Super
如果你是生产者,提供数据的,被add的,或是别人从你这取数据的,使用Extends,放低你的姿态,让别人能add到你,拿到你的数据
如果你是消费者,取数据的,要add别人的,取别人数据的,使用Super,抬高位置,好能add到别人
in和out也同理