Java
Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。
下面这个类为例子
public class FatherClass {
void fatherMethod() {
System.out.println("父类方法");
}
}
public class ExtendsClass {
T t;
void set(T t) {
this.t = t;
}
T get() {
return t;
}
}
在main方法中调用
public static void main(String[] args) {
ExtendsClass sonClassExtendsClassNo = new ExtendsClass();
sonClassExtendsClassNo.set(new SonClass());
SonClass sonClassNo = (SonClass) sonClassExtendsClassNo.get();
ExtendsClass sonClassExtendsClass = new ExtendsClass<>();
sonClassExtendsClass.set(new SonClass());
SonClass sonClass = sonClassExtendsClass.get();
}
第一种:当我们不指定泛型类型时,使用时需要强制转换,这个存在强制转换隐患错误;
第二种:创建时指定类型,获取值的时候就不需要转换了,可以在编译时候检查类型安全,可以用在类,方法,接口上。
第二种就体现了泛型的好处
上界通配符 < ? extends T>
用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
- 必须传入T或者T的子类
- 可以使用T里面的方法
public void upFun(ArrayList extends FatherClass> fatherList) {
}
下界通配符 < ? super T>
用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object。
- 必须传入T或者T的父类
public void downFun(ArrayList super SonClass> fatherList) {
}
无界通配符?
参数可为任何类型,类似于 extends Object>
ArrayList> fatherList
?和 T 的区别
T t;//可以
void set(T t)
public class ExtendsClass
? v;//不可以
public void upFun(ArrayList extends FatherClass> fatherList)
public void downFun(ArrayList super SonClass> fatherList)
public class ExtendsClass extends FatherClass> //不可以
T 是一个确定的类型,通常用于泛型变量、泛型方法和泛型类的定义;
?是一个不确定的类型,通常用于泛型方法形参范围控制,不能用于定义类和泛型方法。
泛型多重限定
首先定义两个接口
interface InterfaceA {
}
interface InterfaceB {
}
继承这两个接口
public class SonClass extends FatherClass implements InterfaceA, InterfaceB {
void sonMethod() {
System.out.println("子类方法");
}
}
泛型类实现
public class InterfaceClass {
T t;
void set(T t) {
this.t = t;
}
T get() {
return t;
}
}
//不可以
InterfaceClass interfaceClassNo = new InterfaceClass();
//可以
InterfaceClass interfaceClass = new InterfaceClass();
总结:当采用双重泛型限定,泛型类必须实现指定需要实现的接口,缺一不可;
协变与逆变
逆变与协变用来描述类型转换(type transformation)后的继承关系,其定义:如果A、B表示类型,f(⋅)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类)
f(⋅)是逆变(contravariant)的,当A≤B时有f(B)≤f(A)成立;
f(⋅)是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;
f(⋅)是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。
--引自网络
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Bird extends Animal {}
协变
ArrayList extends Animal> animalExtends = new ArrayList();
//编译不通过
animalExtends.add(new Dog());
animalExtends.add(new Cat());
animalExtends.add(new Bird());
ArrayList dogList = new ArrayList();
dogList.add(new Dog());
dogList.add(new Dog());
dogList.add(new Dog());
animalExtends = dogList;
//获取到父类
Animal animal = animalExtends.get(0);
逆变
ArrayList super Dog> animalSuper = new ArrayList();
animalSuper.add(new Dog());
//编译不通过
animalSuper.add(new Cat());
animalSuper.add(new Bird());
ArrayList dogList = new ArrayList();
dogList.add(new Dog());
dogList.add(new Dog());
dogList.add(new Dog());
animalSuper = dogList;
//获取集合中的值
Object object = animalSuper.get(0);
- 协变:extends/向上转换/不能add/只能get(T及父类)
- 逆变:super/向下转换/不能get/只能add(T及子类)
Kotlin
学习了一下Java的泛型,Kotlin就简单多了
变量、方法和类的泛型
public class ExtendsClass ->
public class ExtendsClass {
var t: T? = null
fun set(t: T) {
this.t = t
}
fun get(): T? {
return t
}
}
Kotlin 上界通配符
//Java
ArrayList extends FatherClass>
//Kotlin
ArrayList
Kotlin 下界通配符
//Java
ArrayList super SonClass>
//Kotlin
ArrayList
泛型多重限定
//Java
public class InterfaceClass
//Kotlin
class InterfaceClass where T : InterfaceA?, T : InterfaceB?
这里where是个新的关键字
无界通配符*
参数可为任何类型,类似于
ArrayList<*>
问题衍生
kotlin中inline内联函数,为了使调用函数可以内联函数的泛型的类型,可以reified关键字修饰
泛型类型方法
inline fun method(param: T): T {
return param
}
reified关键字修饰,此时泛型T被实化可以获取泛型实际类型,T::class.java实际类型
inline fun method(param: T): T {
return param
}
//可以获取
inline fun getGenericType() = T::class.java
val genericType = getGenericType()
println(genericType)
class java.lang.String
协变与逆变
总体跟Java相同,只是变了一下符合
协变
//Java
ArrayList extends FatherClass>
//Kotlin
ArrayList
逆变
//Java
ArrayList super SonClass>
//Kotlin
ArrayList