Java 与 Kotlin 泛型

Java

Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。

下面这个类为例子

public class FatherClass {
    void fatherMethod() {
        System.out.println("父类方法");
    }
}
public class ExtendsClass<T extends FatherClass> {
    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<SonClass> 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<T extends FatherClass>

? 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 extends InterfaceA & InterfaceB> {
    T t;

    void set(T t) {
        this.t = t;
    }

    T get() {
        return t;
    }
}
//不可以
InterfaceClass interfaceClassNo =  new InterfaceClass<FatherClass>();

//可以
InterfaceClass interfaceClass =  new InterfaceClass<SonClass>();

总结:当采用双重泛型限定,泛型类必须实现指定需要实现的接口,缺一不可;

协变与逆变

逆变与协变用来描述类型转换(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<Dog>();
//编译不通过
animalExtends.add(new Dog());
animalExtends.add(new Cat());
animalExtends.add(new Bird());

ArrayList<Dog> dogList = new ArrayList<Dog>();
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<Animal>();
animalSuper.add(new Dog());
//编译不通过
animalSuper.add(new Cat());
animalSuper.add(new Bird());

ArrayList<Dog> dogList = new ArrayList<Dog>();
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<T extends FatherClass>  ->

public class ExtendsClass<T : FatherClass?> {
    var t: T? = null
    fun set(t: T) {
        this.t = t
    }

    fun get(): T? {
        return t
    }
}
Kotlin 上界通配符
//Java
ArrayList<? extends FatherClass> 

//Kotlin
ArrayList<out FatherClass>
Kotlin 下界通配符
//Java
ArrayList<? super SonClass> 

//Kotlin
ArrayList<in SonClass>
泛型多重限定
//Java
public class InterfaceClass<T extends InterfaceA & InterfaceB> 

//Kotlin
class InterfaceClass<T> where T : InterfaceA?, T : InterfaceB?

这里where是个新的关键字

无界通配符*

参数可为任何类型,类似于<out Any>

 ArrayList<*>
问题衍生

kotlin中inline内联函数,为了使调用函数可以内联函数的泛型的类型,可以reified关键字修饰

泛型类型方法

inline fun <T> method(param: T): T {
   return param
}

reified关键字修饰,此时泛型T被实化可以获取泛型实际类型,T::class.java实际类型

inline fun <reified T> method(param: T): T {
    return param
}

//可以获取
inline fun <reified T> getGenericType() = T::class.java
val genericType = getGenericType<String>()
println(genericType)

class java.lang.String
协变与逆变

总体跟Java相同,只是变了一下符合
协变

//Java
ArrayList<? extends FatherClass> 

//Kotlin
ArrayList<out FatherClass>

逆变

//Java
ArrayList<? super SonClass> 

//Kotlin
ArrayList<in SonClass>

完结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在日常编程中, 我们经常会用到泛型, 用的时候感觉并不复杂, 然而最近在做Kotlin开发时, 被其中的逆变和协变...
    AssIstne阅读 754评论 0 0
  • 什么是泛型? 泛型是一种参数化类型,将操作的数据类型(非基本类型)作为一个参数,这种参数类型可以在类、接口、方法中...
    isLJli阅读 324评论 0 2
  • 1. Java中泛型擦除 Java的泛型并不是真正的范型,而是编译期泛型,在编译完成之后泛型就不存在了,签名中的范...
    把问题说简单点阅读 404评论 0 0
  • 泛型产生 Java泛型是JDK1.5引入的一个新特性,是一种参数化类型。参数化类型就是在不创建新类型的情况下,通过...
    kevinsEegets阅读 627评论 3 1
  • 泛型 由于上方在java中不允许,所以在java中使用通配符?的解决方案 以下View是TextView的父类,T...
    Codes作业本阅读 678评论 0 3