Java 泛型基础

目录

[1. 为什么使用泛型](#1. 为什么使用泛型)

[2. 泛型的使用方式](#2. 泛型的使用方式)

[2.1. 泛型类](#2.1. 泛型类)

[2.2. 泛型接口](#2.2. 泛型接口)

[2.3. 泛型方法](#2.3. 泛型方法)

[3. 泛型涉及的符号](#3. 泛型涉及的符号)

[3.1. 类型通配符"?"](#3.1. 类型通配符"?")

[3.2. 占位符 T/K/V/E](#3.2. 占位符 T/K/V/E)

[3.3. 占位符T和通配符?的区别。](#3.3. 占位符T和通配符?的区别。)

[4. 泛型不变性](#4. 泛型不变性)

[5. 泛型编译时擦除](#5. 泛型编译时擦除)


1. 为什么使用泛型

Java 为什么使用泛型-CSDN博客

2. 泛型的使用方式

2.1. 泛型类

泛型类是用类型参数定义类的一种方式。这些类型参数在声明类变量或作为方法参数时会被具体的类型所替代。

java 复制代码
public class Box<T> {  
    private T t;  
  
    public void set(T t) { this.t = t; }  
    public T get() { return t; }  
}

2.2. 泛型接口

泛型接口和泛型类的定义方式类似。

java 复制代码
public interface List<E> extends Collection<E> {  
    // ...  
}

2.3. 泛型方法

泛型方法是在方法定义中声明类型参数的方法。

java 复制代码
public static <T> T getFirst(List<T> list) {  
    if (list == null || list.isEmpty()) {  
        return null;  
    }  
    return list.get(0);  
}

3. 泛型涉及的符号

3.1. 类型通配符"?"

如 ?、? extends T、? super T。用于表示未知的类型,或表示某个类型的子类型或超类型。

  • 无界通配符"?":

无界通配符表示未知的类型。当使用无界通配符时,编写时不能往这个通配符表示的集合中存放元素,但是可以从集合中获取元素(并且只能赋值给 Object 类型的变量或是进行类型转换)。这是因为编译器不知道集合中元素的具体类型,所以不能确保放入的元素与集合中已有的元素类型兼容。

运行时 可以赋值对象。

java 复制代码
Class<?> clazz = Class.forName("com.mycompany.myreflect.Student");
  • ? extends T:

表示未知的类型,但它是 T 或 T 的某个子类型。

我们就叫做上界限通配符,upper bounded wildcard。

当你需要读取集合中的元素,并且你知道元素的类型至少是 T 时,可以使用这种通配符。但是,你不能往这个集合中添加元素(除了 null),因为编译器无法确保你要添加的元素与集合中已有的元素类型兼容。

  • ? super T:

表示未知的类型,但它是 T 或 T 的某个超类型。

我们就叫做下界通配符, ​​​lower bounded wildcard。

当你需要向集合中添加元素,并且你知道这些元素的类型是 T 或其子类时,可以使用这种通配符。同时,你也可以从集合中读取元素,但是只能赋值给 Object 类型的变量或是 T 的超类型。

3.2. 占位符 T/K/V/E

T是占位符。其实它同K/V/E是一样的没有任何差别。只是我们的习惯会将它用在不同地方用于区别。

java 复制代码
public class PrinterGen<T> { 
//这个字符T,其实你可以使用你喜欢的字符代替,但是它必须和尖括号配合使用
//...
}

3.3. 占位符T和通配符?的区别。

  • 用途 :泛型主要用于定义可重用的类、接口和方法,其中类型参数在编译时确定。通配符主要用于表示对类型的约束或限制,通常用于泛型方法或泛型类的参数

  • 类型擦除:泛型在编译时会被类型擦除,而通配符在运行时仍然存在,用于表示对类型的约束。

  • 编译时是否确定: 如果编译时可以确定类型的,就可以使用T。而一定要等到运行时 才能确定具体类型的就需要使用?

    java 复制代码
            Class<?> clazz = Class.forName("com.mycompany.myreflect.Student");
            System.out.println(clazz);
    
            Class<Student> clazz = Student.class;
            System.out.println(clazz);

    而申明方法,类型,接口时,只能使用T,不能使用?。也是由于我们申明的方法等,在编译时调用它的地方参数可以是不同的,但是必须是确定的。

4. 泛型不变性

Java中的泛型不变性(Generics Invariance)主要指的是泛型类型在编译时的类型安全性质,它确保了泛型类型在声明和使用时类型的一致性。

这是因为虽然String是Object的子类,但是List<String>并不是List<Object>的子类。

5. 泛型编译时擦除

Java的泛型类型信息是在编译时被擦除的,而不是在运行时。这是Java泛型实现的一个重要特性,称为类型擦除(Type Erasure)。

在编译时,Java编译器会处理泛型代码,生成不包含泛型类型信息的字节码。具体来说,编译器会将泛型类型参数替换为它们的上界(通常是Object,除非明确指定了其他上界),并插入必要的类型转换和类型检查代码以确保类型安全。这个过程被称为类型擦除。

在运行时,Java虚拟机(JVM)加载并运行这些已经过类型擦除的字节码。由于泛型类型信息已经被擦除,JVM不知道也不关心这些类型参数。它只看到普通的类和接口,以及普通的方法调用和字段访问。

因此,虽然泛型提供了类型安全和更好的代码可读性,但它们并不会影响Java程序的运行时行为。泛型主要是一种编译时的语法糖,用于提高代码的可读性和类型安全性,而不会增加任何运行时开销。

源代码

编译后

相关推荐
Allen Bright几秒前
maven概述
java·maven
qystca2 分钟前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
编程重生之路2 分钟前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱3 分钟前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea
今天吃饺子7 分钟前
2024年SCI一区最新改进优化算法——四参数自适应生长优化器,MATLAB代码免费获取...
开发语言·算法·matlab
努力进修12 分钟前
“探索Java List的无限可能:从基础到高级应用“
java·开发语言·list
politeboy12 分钟前
k8s启动springboot容器的时候,显示找不到application.yml文件
java·spring boot·kubernetes
Daniel 大东1 小时前
BugJson因为json格式问题OOM怎么办
java·安全
Ajiang28247353042 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
幽兰的天空2 小时前
Python 中的模式匹配:深入了解 match 语句
开发语言·python