泛型
泛型,就是指在定义数据结构时,只指定类型的占位符,待到使用该数据结构时再指定具体的数据类型:
public class Box<T> {
private T t;
public Box(T t) {
this.t = t;
}
}
Box<Integer> box=new Box(2);
在Kotlin中同样也支持泛型,下面是Kotlin实现上面同样的功能:
class Box<T>(t: T) {
var value = t
}
var box: Box<String> = Box("haha")
Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:
private <T> void aa(T m){
}
fun <T> doPrintln(content: T) {
when (content) {
is Int -> println("整型数字为 $content")
is String -> println("字符串转换为大写:${content.toUpperCase()}")
else -> println("T 不是整型,也不是字符串")
}
}
泛型型变
1.Java中的逆变协变
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void toWork() {
System.out.println("我是工人"+getName()+",我要好好干活!!!");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
新建一个Person类
public class Worker1 extends Person {
public Worker1(String name, int age) {
super(name, age);
}
@Override
public void toWork() {
System.out.println("我是1工人"+getName()+",我要好好干活!!!");
}
}
Worker1继承自Person类,并重写了toWork()方法。
public class Worker2 extends Person {
public Worker2(String name, int age) {
super(name, age);
}
@Override
public void toWork() {
System.out.println("我是2工人"+getName()+",我也要好好干活!!!");
}
}
(1)协变
List<Person> personList = new ArrayList<>();
personList.add(new Person("aaa",11));
personList.add(new Worker1("bbb",12));
personList.add(new Worker2("ccc",13));
因为Worker1和Worker2都是Person的子类,所以上面这段代码这么写是可以的。
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("aaa",11));
personList.add(new Worker1("bbb",12));
personList.add(new Worker2("ccc",13));
List<Worker1> worker1List = new ArrayList<>();
worker1List.add(new Worker1("ddd",14));
List<Worker2> worker2List = new ArrayList<>();
worker2List.add(new Worker2("eee",15));
setWork(personList);
setWork(worker1List);
setWork(worker2List);
}
public static void setWork(List<Person> studentList) {
for (Person o : studentList) {
if (o != null){
o.toWork();
}
}
}
此时会发现,报错了,Worker1和Worker2是Person的子类,但是List《Worker1》和List《Worker2》并不是List《Person》的子类,Java编译器并不认为List《Person》和List《Worker1》或List《Worker2》有任何关系。
那要怎么解决呢?
即Java中的协变:
public static void setWork(List<? extends Person> studentList) {
for (Person o : studentList) {
if (o != null){
o.toWork();
}
}
}
ps:协变的泛型只能获取但不能修改了,比如在List中就只能get而不能add了 ,用的时候一定要注意:
为什么呢?
因为咱们刚才把泛型改成了协变的,意思就是上界咱们定为了Person,但是咱们的类型写的是 ? 编译器并不知道咱们给它的具体类型是什么,只要是继承自Person的类就可以,所以get出的对象肯定是Person的子类型,根据多态的特性,所以能够直接赋值给Person,但是add就不可以了,咱们可能添加的是List或List,还有可能是List,所以编译器无法确定咱们添加的到底是什么类型就无法继续执行了,肯定就报错了。
(2)逆变
逆变和协变是正好相反的。
List<Person> personList = new ArrayList<>();
personList.add(new Person("aaa",11));
personList.add(new Worker1("bbb",12));
personList.add(new Worker2("ccc",13));
List<Worker1> worker1List = new ArrayList<>();
worker1List.add(new Worker1("ddd",14));
setWorker(personList);
setWorker(worker1List);
public static void setWorker(List<Worker1> studentList) {
for (Object o : studentList) {
System.out.println("哈哈 "+o.toString());
}
}
会报错,方法接收的是List《Worker1》,但是传的却有List《Person》,而且 Woker1是Person的子类,而不是Person是Worker1的子类。
那么这种情况下就可以使用逆变了:
public static void setWorker(List<? super Worker1> studentList) {
for (Object o : studentList) {
System.out.println("哈哈 "+o.toString());
}
}
逆变和协变一样,类型也是 ?,不过 ?extends 是上界通配符,而 ?super 是下界通配符,它的范围包括Worker1和它的父类。和协变相反,逆变中是可以add的,因为Worker1一定是这个未知类型的子类型,所以是可以add的(即可以接收所有 Worker1的子类添加至该列表中。)。这里也没有get的限制,会变成Object ,因为在Java中所有类型都是Object的子类。
比如我们再新建一个Worker1的子类Worker3:
public class Worker3 extends Worker1{
public Worker3(String name, int age) {
super(name, age);
}
@Override
public void toWork() {
System.out.println("我是3工人"+getName()+",我是Worker1的子类!");
}
}
那么逆变就可以add Worker1及其子类Worker3了:
public static void setWorker(List<? super Worker1> studentList) {
studentList.add(new Worker1("ddd",15));
studentList.add(new Worker3("ddd",15));
for (Object o : studentList) {
System.out.println("哈哈 "+o.toString());
}
}
2.kotlin中的逆变协变
Kotlin的逆变和协变和Java中的类似,Java泛型中有类型通配符这一机制,不过在Kotlin泛型中,没有通配符,取而代之的是out和in关键字。
(1)kotlind的协变-out
kotlin版的Person,Worker1,Worker2
open class Person(var name: String, var age: Int) {
open fun toWork() {
println("我是工人$name,我要好好干活!!!")
}
}
class Worker1(name: String, age: Int) : Person(name, age) {
override fun toWork() {
println("我是1工人$name,我要好好干活!!!")
}
}
class Worker2(name: String, age: Int) : Person(name, age) {
override fun toWork() {
println("我是2工人$name,我也要好好干活!!!")
}
}
协变:
fun main() {
val personArrayList: MutableList<Person> = ArrayList()
personArrayList.add(Person("aaa", 11))
personArrayList.add(Worker1("bbb", 12))
personArrayList.add(Worker2("ccc", 13))
val personArrayList1: MutableList<Worker1> = ArrayList()
personArrayList1.add(Worker1("ddd", 14))
val personArrayList2: MutableList<Worker2> = ArrayList()
personArrayList2.add(Worker2("eee", 15))
setWork(personArrayList)
setWork(personArrayList1)
setWork(personArrayList2)
}
fun setWork(studentList: List<out Person>) {
for (o in studentList) {
o.toWork()
}
}
仔细看下setWork()这个方法,这个方法提醒咱们这里的out关键字可以省略掉。
因为Kotlin中List是只读的,所以说肯定是安全的,所以官方在定义List接口的时候就直接定义成了协变的:
(2)kotlin的逆变-in
val personArrayList: MutableList<Person> = ArrayList()
personArrayList.add(Person("aaa", 11))
personArrayList.add(Worker1("bbb", 12))
personArrayList.add(Worker2("ccc", 13))
val personArrayList1: MutableList<Worker1> = ArrayList()
personArrayList1.add(Worker1("ddd", 14))
setWorker(personArrayList)
setWorker(personArrayList1)
fun setWorker(studentList: MutableList<in Worker1>) {
for (o in studentList) {
println("哈哈 " + o.toString())
}
}
fun setWorker(studentList: MutableList<in Worker1>) {
studentList.add(Worker1("ddd", 15))
for (o in studentList) {
println("哈哈 " + o.toString())
}
}
总结: kotlin的协变逆变同Java的类似,java中有通配符机制 ?extends T(包括T和它的子类)和?super T(包括T和它的父类);但kotlin中是通过out,in关键字实现。协变中不可以add,逆变中可以add T及其子类。