4.1 泛型:类型安全
对比java的历史,java1.5 开始引入的 泛型。同样,Kotlin泛型也是很重要的
泛型的优势:
- 类型检查,能再编译的时候帮你检查错误
- 自动类型转换,获取数据的时候不需要进行类型强制转换
(更加语义话,能够写出更加通用的代码)
4.2 使用
假设有一个小的需求使用:
定义一个find方法,传入一个对象,若列表中存在该对象,则返回对象,不存在则返回空
由于原有的集合类不存在这样一个方法,所以可以定义一个新的集合类,同样要声明泛型。
class SmartList<T>: ArrayList<T>() {
fun find(t:T):T?{
val index = super.indexOf(t)
return if(index >= 0) super.get(index) else return null
}
}
fun main() {
val smartList = SmartList<String>()
smartList.add("one")
println(smartList.find("one"))
println(smartList.find("two").isNullOrEmpty())
}
除了定义泛型,还可以利用泛型扩展函数来实现
fun<T> ArrayList<T>.find(t:T):T?{
val index = this.indexOf(t)
return if(index >= 0) this.get(index) else return null
}
fun main() {
// val smartList = SmartList<String>()
val smartList = ArrayList<String>()
smartList.add("one")
println(smartList.find("one"))
println(smartList.find("two").isNullOrEmpty())
}
kotlin中,var arraylist = ArrayList(); 是错误不被允许的。Java中可以这么做原因是Java1.5引入泛型,为了向前兼容List list = new ArrayList() 这种存在线上很久的代码。而Kotlin是基于Java1.6 所以没有兼容问题,这么做就不允许。
Kotlin中还有类型推导,所以 var arraylist = arrayListOf("one","two") 这样的方式是可以的。
4.3 类型约束:设定类型上下界
定义一个盘子,可以存放任何东西
class Plate<T>(val t:T)
我们想给Plate归类,一类放水果一类放蔬菜
定义水果
open class Fruit(val weight:Double)
class Apple(weight: Double):Fruit(weight)
class Banana(weight: Double):Fruit(weight)
定义水果盘子类型
class FruitPlate<T:Fruit>(val t:T)
两种实例写法
val applePlate = FruitPlate<Apple>(Apple(100.0))
val applePlate2= FruitPlate(Apple(100.0))
和java类似,java语法用extends,kotlin 中用“:”,这种类型的泛型约束我们称之为 上界约束
我们会出现这种情况
val applePlate3 =FruitPlate(null)
上面声明的应该是一个参数不可空的类型,实例报错,而我们的果盘是可以空着的 ,那么我们应该改成
class FruitPlate<T:Fruit?>(val t:T)
上面是类型约束都是单个条件,比如类型上界是什么,是否可空。那么多个条件如何处理-----where 关键字
有一把刀可以用来切水果,我们可以实现如下:
interface Ground{}
class Watermelon(weight: Double):Fruit(weight),Ground
fun <T> cut(t:T) where T:Fruit,T:Ground{
println("you can cut me")
}
cut(Watermelon(100.0)) // 允许
// cut(Apple(2.0))//不允许
4.4泛型的背后:类型擦除
Java 无法声明一个泛型数组
思考:Apple[] 和 Fruit[]
以及 List<Apple> 和 List<Fruit>
Apple[] appleArray = new Apple[10];
Fruit[] fruitArray = appleArray;// 允许
fruitArray[0] = new Banana(0.5);// 编译通过,运行报ArrayStoreException
List<Apple> appleList=new ArrayList<Apple>();
List<Fruit> fruitList = appleList;// 不允许
数组是协变的,而List是不变的
Object[] 是所有对象数组的父类,List<Object> 和 List<T> 没有这种父子关系
java的泛型是类型擦除的,是一个伪泛型。代码对比一下
System.out.println(appleArray.getClass());
System.out.println(appleList.getClass());
运行结果
class [Ljavat.Apple;
class java.util.ArrayList
说明数组不支持泛型
Kotlin 中的泛型机制和java一样,所以上面特性在kotlin中同样存在。
但不同的是,Kotlin 中数组支持泛型,不再协变
不能将任意一个对象数组赋值给Array<Any> 或者 Array<Any?>
val appleArray = arrayOfNulls<Apple>(3)
// var anyArray:Array<Any?> = appleArray//不允许
我们看段代码
ArrayList list = new ArrayList();
ArrayList<String> stringList = new ArrayList<String>();
String s =stringList.get(0);
ASM反编译后
public <init>()V
。。。
L1
LINENUMBER 8 L1
ALOAD 0
NEW java/util/ArrayList
DUP
INVOKESPECIAL java/util/ArrayList.<init> ()V
PUTFIELD top/zcwfeng/java/base/数组协变.list : Ljava/util/ArrayList;
L2
LINENUMBER 9 L2
ALOAD 0
NEW java/util/ArrayList
DUP
INVOKESPECIAL java/util/ArrayList.<init> ()V
PUTFIELD top/zcwfeng/java/base/数组协变.stringList : Ljava/util/ArrayList;
L3
LINENUMBER 11 L3
ALOAD 0
ALOAD 0
GETFIELD top/zcwfeng/java/base/数组协变.stringList : Ljava/util/ArrayList;
ICONST_0
INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;
CHECKCAST java/lang/String
PUTFIELD top/zcwfeng/java/base/数组协变.s : Ljava/lang/String;
RETURN
。。。
发现L1,L2 声明ArrayList 编译后字节码是一样的。可见类型擦除了。泛型类型自动转换如何呢?L3 字节码中我们看到 他是类型强制转换了。虽然类型擦除了,但是还是通过其他方式保证了泛型的额相关特性。
但是有些时候我们需要获取运行时候的 泛型参数类型,比如序列化和反序列化。
open class Plate1<T>(val t:T,val clazz:Class<T>){
fun getType(){
println(clazz)
}
}
val applePlate1 = Plate1(Apple(1.0),Apple::class.java)
applePlate1.getType()
使用这种方式获取到了,但是如下不可以
// val listType = ArrayList<String>::class.java //不允许
// val mapType = ArrayList<String,String>::class.java//不允许
还有另外的方式可以获取各种类型信息?利用匿名内部类
val list1 =ArrayList<String>()
val list2= object : ArrayList<String>(){}// 匿名内部类
println(list1.javaClass.genericSuperclass)
println(list2.javaClass.genericSuperclass)
输出:
java.util.AbstractList<E>
java.util.ArrayList<java.lang.String>
原因,类型擦除不会将全部的类型信息都擦除,还会 将类型信息放在class常量池中。
设计 一个获取所有类型信息的类
// 获取所有类型信息
open class GenericsToken<T>{
var type: Type = Any::class.java
init {
val superClass = this.javaClass.genericSuperclass
type = (superClass as ParameterizedType).actualTypeArguments[0]
}
}
val gt =object :GenericsToken<Map<String,String>>(){}
println(gt.type)
输出:
java.util.Map<java.lang.String, ? extends java.lang.String>
通过获取父类或者父类接口泛型类型信息实现我们的需求。我们长用的Gson也是使用了相同的设计。
val json = Gson().fromJson<List<String>>("json 串。。。")
val rType = object : TypeToken<List<String>>(){}.type //利用泛型擦除遗留的信息,内部类保存了外部类的信息,class的常量池
val json2 = Gson().fromJson<List<String>>("json 串。。。",rType)
使用函数内联获取泛型
inline fun <reified T>getType(){
return T::class.java
}
// reified 修饰的inline 方法只有用在kotlin中,java无法调用
inline fun <reified T:Any> Gson.fromJson(json:String):T{
return Gson().fromJson(json,T::class.java)
}
kotlin普通内联函数可以在Java中调用,他会被当一个普通常规函数,但是reified实例化的参数类型的内联函数则不能被java调用。因为他一直是内联的。
4.5 kotlin中的泛型,协变和逆变
java 中
List<String> mstringList = new ArrayList<String>();
// List<Object> objectList = mstringList;// 报错
不能直接赋值,如果可以这样会导致类型安全问题。java泛型明确泛型基本条件是保证安全,所以不支持这种。
kotlin 中
val stringList:List<String> = ArrayList<String>();
val anyList:List<Any> = stringList
List 源码
public interface List<out E> : Collection<E> {
......
}
泛型前面多了一个out关键字
正常java 中List<String> 不是List<Object> 子类。Kotlin也是这样的规则。但是out 声明后,Kotlin的List支持了协变(类型A是类型B的子类型那么,Generic<A> 也是 Generic<B>的子类型)
// 泛型协变
val stringList:List<String> = ArrayList<String>();
// stringList.add("kotlin")// 编译报错 <out E> 只读
支持协变的List只可以读取,不能添加
类似java中 < ? extends Object>
List<? extends Object> mList = new ArrayList<String>();
通常情况一个泛型Generic<T> 支持协变,那么他里面的方法的参数类型不能使用T类型,因为一个方法的参数不能传入参数父类型对象,那样可能导致错误。
泛型不变和协变,还有第三种逆变(类型A是类型B的子类型反过来Generic<B> 是Generic<A>的子类型)
现在对 MutableList<Double>排序
val doubleComparator = Comparator<Double>{
d1,d2->d1.compareTo(d2)
}
// 逆变
val doubleList = mutableListOf(2.0,3.0)
doubleList.sortWith(doubleComparator)
暂时看没什么问题那么如果intComparator,longComparator.用Number 替换子类型
val numberComparator = Comparator<Number> {
d1,d2 -> d1.toDouble().compareTo(d2.toDouble())
}
// 逆变
val doubleList = mutableListOf(2.0, 3.0)
doubleList.sortWith(numberComparator)
val intList = mutableListOf(2,1)
intList.sortWith(numberComparator)
看下sortWith源码
expect fun <T> MutableList<T>.sortWith(comparator: Comparator<in T>): Unit
in 关键字,逆变(如果A是B的 子类型,那么反过来Genric<B>是Generic<A>的子类型)
和java中的<? super T> 相同的效果。
通配符*
MutableList<*>与MutableList<Any?> 不是统一中列表
MutableList<*> 相当于------> MutableList<out Any?>
网友评论