本文主要是介绍泛型之不变、协变、逆变,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
什么是泛型
早期在java1.5之前,java集合类是没有泛型的、集合内存储的对象都是object。这就导致了几个问题。
1.存储的元素取出来、需要类型强制转换、而且不能保证其准确性、如下图,改元素第一个需要转换成String 类型、第二个元素要转成Integer类型。代码编写很容易出问题
ArrayList<Object> objects = new ArrayList();objects.add("hello world");objects.add(new Integer(1));// 需要强转Object o = objects.get(0);//此处运行报错Integer index0 = (Integer)objects.get(0);
2.存储元素不能在编译时,提示错误,直到运行时才能抛出错误
为了解决以上问题,java1.5之后就提出了泛型的概念、泛型是一种标记在类,或者方法上的类型,他并不代表真实的类型,代表着一种行为约束
泛型的作用
ArrayList<CharSequence> charSequences = new ArrayList<CharSequence>();charSequences.add("hello world");//编译报错charSequences.add(1.2);CharSequence charSequence = charSequences.get(0);
1.上面示例代码可以看出、在编译时,如果类型不是泛型类型,直接编译不通过
2.取出元素、不需要强制转换
3. 可以让代码抽象程度更高
java中的泛型为何要在编译后做泛型擦出
1、java1.5之后的版本,需要兼容之前没有泛型的版本、编译之后和1.5之前编译之后的字节码保持一致
ArrayList<CharSequence> charSequences = new ArrayList<CharSequence>();ArrayList<String> charSequences = new ArrayList<String>();
编译后字节码一致
2、擦出之后如何避免强制转换的
如下是list get方法的源码、直接强制转换成泛型类型
泛型的上界 和下界、对泛型做约束
java 中 使用的 super ,extends
kotlin 中使用 :
如果需要约定多个类型kotlin 可以使用where 关键字
// 泛型 上界约束
open class Fruit(val weight: Double)
class Apple(weight: Double) : Fruit(weight)
class Banana(weight: Double) : Fruit(weight)open interface Animal{}
class Fish( weight: Double):Animal
class Cat( weight: Double):Animalclass Monkey(weight: Double): Fruit(weight),Animal// 容器约定必须为fruit 子类
class Container<T:Fruit>(t:T)//只能继承一个类,实现多个接口
class Box<T> (t:T) where T:Fruit , T:Animal
什么是不变
泛型不变,就类、或者方法泛型、泛型的类型 定义,不可以使用其子类或者父类来接受、java中的集合都是泛型不变的,例如 CharSequence 是String 类型的父类
ArrayList<CharSequence> charSequences = new ArrayList<CharSequence>();// 不允许// ArrayList<CharSequence> charSequences2 = new ArrayList<String>();
什么是协变
java 中典型的例子就是数组、Fruit 是Apple 的父类、子类的类型可以用父类的类型接受
// 协变 A是B 的父类 ,那么 泛型就是泛型的父类
// 允许Fruit[] arr = new Apple[10];
在koltin 中、我们可以使用 out 关键字,打破集合的不变,实现集合的协变
这里使用out 关键字,可以使用父类泛型接受子类类型
什么是逆变
// 协变 A是B 的父类 ,那么 范型A 就是泛型B的子类
// 容器约定必须为fruit 子类
class Container2<in T>(t:T)class Container3<in T>(t:T)//只能继承一个类,实现多个接口
class Box<T> (t:T) where T:Fruit , T:Animalfun <T> hello(t:T) where T:Fruit ,T:Animal{}fun check(){// outvar father = Container<Fruit>(Fruit(1.2))father = Container<Apple>(Apple(1.2))// in 子类类型接受父类类型var son = Container2<Banana>(Banana(1.2))son = Container2<Fruit>(Fruit(1.2))var fish = Container3<Fish>(Fish(1.2))fish = Container3<Animal>(object :Animal{})
}
泛型形变的应用场景
1.需求:将double 数组拷贝到另外一个数组、代码实现如下
//拷贝double
fun copy(dest:Array<Double?>,src:Array<Double>){if (dest.size< src.size){throw IndexOutOfBoundsException()}else{src.forEachIndexed { index, d -> dest[index] = d }}
}
2.需求:如果上述拷贝的是Int ,是不是要写一个Int 类型的方法,显然不合适,于是呼我们使用泛型写出如下代码,这样拷贝任意类型都可以使用如下方法
//拷贝任意类型
fun <T> copyUseTypeRefrence(dest:Array<T?>,src:Array<T>){if (dest.size< src.size){throw IndexOutOfBoundsException()}else{src.forEachIndexed { index, d -> dest[index] = d }}
}
3.需求:如果拷贝子类,用父类接受,比如拷贝apple数组 到 Fruit 数组中、我们就需要用到变型
//拷贝任意类型
// 拷贝父子类型 协变 out 版本 、返回值,提供者
fun <T> copyUseOut(dest:Array<T?>,src:Array< out T>){if (dest.size< src.size){throw IndexOutOfBoundsException()}else{src.forEachIndexed { index, d -> dest[index] = d }}
}// 拷贝父子类型 逆变in 版本 ,入参、消费者
fun <T> copyUseIn(dest:Array< in T?>,src:Array< T>){if (dest.size< src.size){throw IndexOutOfBoundsException()}else{src.forEachIndexed { index, d -> dest[index] = d }}
}
测试代码
fun main() {val src = arrayOf(1.0, 2.0, 3.0)val dest = arrayOfNulls<Double>(3)copy(dest, src )println("普通拷贝")dest.forEach { print(it)}println()val src2 = arrayOf(1.0, 2.0, 3.0)val dest2 = arrayOfNulls<Double>(3)copyUseTypeRefrence(dest2, src2 )println("泛型拷贝")dest2.forEach { print(it)}println()val src3 = arrayOf(Apple(1.0), Apple(2.0), Apple(3.0))val dest3 = arrayOfNulls<Fruit>(3)copyUseOut(dest3, src3 )println("泛型拷贝out")dest3.forEach { print(it)}println()val src4 = arrayOf(Apple(1.0), Apple(2.0), Apple(3.0))val dest4 = arrayOfNulls<Fruit>(3)copyUseIn(dest4, src4 )println("泛型拷贝in")dest4.forEach { print(it)}println()}
测试结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=64273:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/rt.jar:/Users/yang/gitcode/kotlin/out/production/kotlin:/Users/yang/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.10/kotlin-stdlib-jdk8-1.7.10.jar:/Users/yang/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.7.10/kotlin-stdlib-1.7.10.jar:/Users/yang/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.10/kotlin-stdlib-common-1.7.10.jar:/Users/yang/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/Users/yang/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.10/kotlin-stdlib-jdk7-1.7.10.jar TypeParamKt
普通拷贝
1.02.03.0
泛型拷贝
1.02.03.0
泛型拷贝out
Apple(weight=1.0)Apple(weight=2.0)Apple(weight=3.0)
泛型拷贝in
Apple(weight=1.0)Apple(weight=2.0)Apple(weight=3.0)Process finished with exit code 0
代码:https://gitcode.net/mid120/kotlin/-/commit/718964acc4d7ce36970564ff0755ec47f6939edf?spm=1033.2243.3001.5872
这篇关于泛型之不变、协变、逆变的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!