_Kotlin_系列_ 二、Kotlin泛型,使用指南

本文深入探讨了Kotlin的泛型,包括Java泛型的基础,如简单使用、泛型擦除、边界、通配符及其在运行时的行为。通过示例解释了如何定义和使用泛型,以及如何通过反射获取泛型信息。此外,文章还讨论了泛型的边界限制、通配符的协变与逆变,以及PECS原则在泛型设计中的应用。
摘要由CSDN通过智能技术生成

2、将运行期的类型转换异常提前到了编译期,保证类型的安全,避免类型转换异常

3、怎么去定义和使用泛型?

我们可以给一个类,方法,或者接口指定泛型,在具体使用的地方指定具体的类型

一、Java 泛型

要学习好 Kotlin 泛型,我们先要对 Java 泛型足够的了解,因为 Kotlin 泛型和 Java 泛型基本上是一样的,只不过在 Kotlin 上有些东西换了新的写法

1、泛型的简单使用

在 Java 中,我们可以给一个类,方法,或者接口指定泛型,在具体使用的地方指定具体的类型

1)、定义一个泛型类,在类名的后面加上 <T> 这种语法结构就是定义一个泛型类,泛型可以有任意多个

//定义一个泛型类
public class JavaGenericClass<T> {

    private T a;

    public JavaGenericClass(T a) {
        this.a = a;
    }

    public T getA() {
        return a;
    }

    public void setA(T a) {
        this.a = a;
    }

    //泛型类使用
    public static void main(String[] args) {
      	//编译器可推断泛型类型,因此 new 对象后面的泛型类型可省略
        JavaGenericClass<String> javaGenericClass1 = new JavaGenericClass<String>("erdai");
        JavaGenericClass<Integer> javaGenericClass2 = new JavaGenericClass<>(666);
        System.out.println(javaGenericClass1.getA());
        System.out.println(javaGenericClass2.getA());
    }
}

//打印结果
erdai
666 

2)、定义一个泛型方法,在方法的返回值前面加上 <T> 这种语法结构就是定义一个泛型方法,泛型可以有任意多个,泛型方法的泛型与它所在的类没有任何关系

public class JavaGenericMethod {

    public <T> void getName(T t){
        System.out.println(t.getClass().getSimpleName());
    }

    public static void main(String[] args) {
        JavaGenericMethod javaGenericMethod = new JavaGenericMethod();
      	//编译器可推断出泛型类型,因此这里的泛型类型也可省略
        javaGenericMethod.<String>getName("erdai666");
    }
}

//打印结果
String 

3)、定义一个泛型接口

在接口名的后面加上 <T> 这种语法结构就是定义一个泛型接口,泛型可以有任意多个

public interface JavaGenericInterface<T> {

    T get();
}

class TestClass<T> implements JavaGenericInterface<T>{

    private final T t;

    public TestClass(T t) {
        this.t = t;
    }

    @Override
    public T get() {
        return t;
    }
}

class Client{

    public static void main(String[] args) {
        JavaGenericInterface<String> javaGenericInterface = new TestClass<>("erdai666");
        System.out.println(javaGenericInterface.get());
    }
}
//打印结果
erdai666 

2、泛型擦除

1、泛型擦除是什么?

看下面这段代码:

//使用了不同的泛型类型 结果得到了相同的数据类型
public class JavaGenericWipe {
  
    public static void main(String[] args) {
        Class a = new ArrayList<String>().getClass();
        Class b = new ArrayList<Integer>().getClass();

        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("a == b: " + (a == b));
    }
}

//打印结果
a = class java.util.ArrayList
b = class java.util.ArrayList
a == b: true 

为啥会出现这种情况呢?

因为 Java 中的泛型是使用擦除技术来实现的:泛型擦除是指通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上

之所以要使用泛型擦除是为了兼容 JDK 1.5 之前运行时的类加载器,避免因为引入泛型而导致运行时创建不必要的类

2、泛型擦除的具体步骤

1)、擦除所有类型参数信息,如果类型参数是有界的,则将每个参数替换为其第一个边界;如果类型参数是无界的,则将其替换为 Object类型擦除的规则:

<T> 擦除后变为 Object

<T extends A> 擦除后变为 A

<? extends A> 擦除后变为 A

<? super A> 擦除后变为Object

2)、(必要时)插入类型转换,以保持类型安全

3)、(必要时)生成桥接方法以在子类中保留多态性

//情况1: 擦除所有类型参数信息,如果类型参数是有界的,则将每个参数替换为其第一个边界;如果类型参数是无界的,则将其替换为 Object
class Paint {
    void draw() {
        System.out.println("Paint.draw() called");
    }
}

//如果不给 T 设置边界,那么 work 方法里面的 t 就调用不到 draw 方法
class Painter<T extends Paint> {
    private T t;

    public Painter(T t) {
        this.t = t;
    }
    public void work() {
        t.draw();
    }
}

//情况2:(必要时)插入类型转换,以保持类型安全
public class JavaGenericWipe {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("erdai");
        stringList.add("666");

        for (String s : stringList) {
            System.out.println(s);
        }
    }
}

//编译时生成的字节码文件翻译过来大致如下
public class JavaGenericWipe {
    public JavaGenericWipe() {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值