Java泛型是什么?

简介: 本文回顾了作者五年的工作经历,强调了自我学习的重要性,并介绍了Java泛型的基础知识,包括泛型的概念、泛型集合、泛型方法、泛型接口、泛型类及类型擦除等内容,旨在帮助读者理解泛型机制及其在编程中的应用。

1、 文章背景

工作已有五年之久,回望过去,没有在一线城市快节奏下学习成长,只能自己不断在工作中学习进步,最近一直想写写属于自己的文章,记录学习的内容和知识点,当做一次成长。

2、 泛型的概述

摘要:Java泛型是JDK5中引入的一个新特性,其本质是参数化类型‌。

什么是泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参列表,普通方法的形参列表中,每个形参的数据类型是确定的,而变量是一个参数。在调用普通方法时需要传入对应形参数据类型的变量(实参),若传入的实参与形参定义的数据类型不匹配,则会报错。

Java 1.5 之前没有泛型,通常需要使用强制类型转换的方式将一种数据类型转换为另一种数据类型,这种转换要求开发者对实际参数的类型具有可预知性。对于强制类型转换错误的情况,编译器可能不会提示错误,但是在运行时会出现异常,这是一个安全隐患。

为了解决这一隐患,从 Java 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

3、泛型集合

泛型本质上是提供类型的“类型参数”,也就是参数化类型。我们可以为类、接口或方法指定一个参数类型,通过这个操作限制数据操作的类型,从而保证类型转换的绝对安全。

代码Map<Integer,Book> books=new HashMap<Integer,Book>();创建了一个键类型为 Integer、值类型为 Book 的泛型集合,即指明了该 Map 集合中存放的键必须是 Integer 类型、值必须为 Book 类型,否则编译出错。在获取 Map 集合中的元素时,不需要将books.get(id);获取的值强制转换为 Book 类型,程序会隐式转换。在创建 List 集合时,同样使用了泛型,因此在获取集合中的元素时也不需要将bookList.get(i)代码强制转换为 Book 类型,程序会隐式转换。

4、泛型方法

泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static 的方法而言,无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

  • 4.1 方法中形参类型不确定时
  • 使用类后面定义的泛型。
  • 在方法上定义自己的泛型。

  • 4.2 格式

ini

代码解读

复制代码

[访问权限修饰符][static][final]<类型参数列表> 返回值类型 方法名([形式参数列表]){};

  • 4.3 多个类型参数
  • 泛型方法可以定义多个类型参数。

java

代码解读

复制代码

public static <T, U> void printPair(T first, U second) {
    System.out.println(first + ", " + second);
}
  • 4.4 参数类型约束
  • 可以通过extends关键字对类型参数进行约束,要求类型参数必须是某个类(或接口)的子类(或实现类)。

java

代码解读

复制代码

public static <T extends Number> void printNumber(T num) {
    System.out.println(num);
}

5、泛型接口

泛型接口是Java泛型机制的一部分,它允许在接口定义中使用类型参数,使得实现该接口的类或方法在遵循接口规范的同时,可以处理不同的数据类型。

  • 5.1 格式
  • 泛型接口的定义与泛型类类似,在接口名后面的尖括号<>中指定类型参数。这些类型参数可以在接口的方法签名、返回类型或参数列表中使用。

java

代码解读

复制代码

public interface GenericInterface<T> {

    void someMethod(T t);
    
    T anotherMethod();
}

  • 5.2 实现泛型接口
  • 在实现泛型接口时,可以选择指定类型参数的具体类型,或者将实现类也定义为泛型类,以便推迟类型参数的指定。

java

代码解读

复制代码

// 指定具体类型
public class ConcreteClass implements GenericInterface<String> {
    @Override
    public void someMethod(String s) {
        // 实现方法
    }

    @Override
    public String anotherMethod() {
        // 返回String类型的值
        return "Hello";
    }
}
// 推迟类型参数的指定
public class GenericClass<T> implements GenericInterface<T> {
    @Override
    public void someMethod(T t) {
        // 实现方法
    }

    @Override
    public T anotherMethod() {
        // 返回T类型的值
        return null; // 或者其他T类型的实例
    }
}

  • 5.3 多个类型参数接口
  • 泛型接口可以定义多个类型参数。

java

代码解读

复制代码

public interface MultipleGenericInterface<T, U> {
    T getFirst();
    U getSecond();
}

  • 5.4 类型参数的约束
  • 在接口中定义泛型,并在实现时指定具体类型。

java

代码解读

复制代码

public interface MultipleGenericInterface<T extends Number> {

    // 省略其他方法
}

6、泛型类

泛型类是Java泛型机制的一个重要组成部分,它允许在类定义时使用类型参数,以便类可以处理不同的数据类型。

  • 6.1 格式
  • 泛型类的定义与普通类相似,但在类名后面的尖括号<>中指定了一个或多个类型参数。这些类型参数可以在类的字段、方法参数、返回类型以及方法体内部使用。

java

代码解读

复制代码

public class GenericClass<T> {
    private T value;

    public GenericClass(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

  • 6.2 实例化泛型类
  • 在创建泛型类的实例时,需要指定类型参数的具体类型。

java

代码解读

复制代码

GenericClass<String> stringInstance = new GenericClass<>("Hello");
GenericClass<Integer> integerInstance = new GenericClass<>(123);
  • 6.3 多个类型参数
  • 泛型类可以定义多个类型参数。

java

代码解读

复制代码

public class MultipleGenericClass<T, U> {
    private T first;
    private U second;

    public MultipleGenericClass(T first, U second) {
        this.first = first;
        this.second = second;
    }

    // 省略getter和setter方法
}

  • 6.4 类型参数的约束
  • 与泛型接口类似,泛型类中的类型参数也可以通过extends(对于类)或implements(对于接口)关键字进行约束。例如,要求类型参数继承自某个类或实现某个接口:

java

代码解读

复制代码

public class ConstrainedGenericClass<T extends Number> {
    private T value;

    public ConstrainedGenericClass(T value) {
        this.value = value;
    }

    // 省略其他方法
}

7、类型擦除

泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即编译器会在编译期间擦除代码中的所有泛型语法并相应的做出一些类型转换动作。

换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。

8、泛型标识

  • 尖括号 <> 中的 泛型标识被称作是类型参数,用于指代任何数据类型。
  • 泛型标识是任意设置的(如果你想可以设置为 Hello都行),Java 常见的泛型标识以及其代表含义如下:

java

代码解读

复制代码

  T :代表一般的任何类。
  E :代表 Element 元素的意思,或者 Exception 异常的意思。
  K :代表 Key 的意思。
  V :代表 Value 的意思,通常与 K 一起配合使用。
  S :代表 Subtype 的意思,文章后面部分会讲解示意。

9、泛型通配符

在Java的泛型机制中,通配符(wildcard)是一种特殊的类型参数,它用于表示未知的类型。泛型通配符主要用于泛型方法的定义中,以及作为泛型类型和泛型方法之间的桥梁,增加代码的灵活性。以下是关于泛型通配符的关键点:

  • 问号(?)
    问号(?)是泛型通配符的基本符号,它表示未知的类型。在泛型方法中,可以使用问号来代替具体的类型参数。
  • 上限通配符(? extends T)
    上限通配符表示未知的类型是T类型或T类型的子类。它用于限制泛型类型的上界,确保泛型方法或泛型集合中的元素是T类型或其子类类型。
    例如,List<? extends Number>表示一个包含Number或其子类(如IntegerDouble等)的列表。
  • 下限通配符(? super T)
    下限通配符表示未知的类型是T类型或T类型的父类。它用于限制泛型类型的下界,允许泛型方法或泛型集合添加T类型或其子类类型的元素,但读取时只能保证是T类型的父类。
    例如,List<? super Integer>表示一个可以包含Integer或其父类(如NumberObject等)的列表,但可以安全地向其中添加Integer类型的元素。


转载来源:https://juejin.cn/post/7428997298196447270

目录
打赏
0
2
2
1
179
分享
相关文章
Spring Get请求 与post请求
本文详细介绍了Spring框架中GET请求和POST请求的区别及应用场景。GET请求用于从服务器获取资源,参数附在URL末尾,适合查看非敏感信息;POST请求用于向服务器提交数据,参数在请求体中传输,适合处理敏感信息。Spring通过`@GetMapping`和`@PostMapping`注解分别处理这两种请求。此外,文章还提供了示例代码,展示了如何在Spring中实现这两种请求的处理。最后,文章总结了推荐使用POST请求的原因,包括更高的安全性、更大的数据传输量、更好的幂等性及灵活性。
301 1
Spring Get请求 与post请求
使用Java创建集成JACOB的HTTP服务
本文介绍了如何在Java中创建一个集成JACOB的HTTP服务,使Java应用能够调用Windows的COM组件。文章详细讲解了环境配置、动态加载JACOB DLL、创建HTTP服务器、实现IP白名单及处理HTTP请求的具体步骤,帮助读者实现Java应用与Windows系统的交互。作者拥有23年编程经验,文章来源于稀土掘金。著作权归作者所有,商业转载需授权。
230 2
使用Java创建集成JACOB的HTTP服务
MySQL 延迟从库介绍
本文介绍了MySQL中的延迟从库功能,详细解释了其工作原理及配置方法。延迟从库允许从库在主库执行完数据变更后延迟一段时间再同步,主要用于快速恢复误操作的数据。此外,它还可用于备份、离线查询及数据合规性需求。通过合理配置,可显著提升数据库系统的稳定性和可靠性。
350 4
Java 8 异步编程利器:CompletableFuture
Java 8引入了CompletableFuture,这是一个强大的异步编程工具,增强了Future的功能,支持链式调用、任务组合与异常处理等特性,使异步编程更加直观和高效。本文详细介绍了CompletableFuture的基本概念、用法及高级功能,帮助开发者更好地掌握这一工具。
136 0
springboot解决跨域问题
跨域问题指前端调用与后端接口不在同一域名或端口时产生的安全限制。本文介绍两种在Spring Boot中解决跨域问题的方法:一是通过配置CorsFilter,二是实现WebMvcConfigurer接口。配置完成后重启项目即可生效。作者:博笙困了。来源:稀土掘金。
126 6
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
310 0
Jackson反序列化不可变类
Jackson 默认的反序列化策略需要无参构造器和字段 setter 函数。对于不可变类(如 `ImmutableUser`),可以通过以下三种方式解决: 1. **使用 Jackson 注解**:在全参构造器上使用 `@JsonCreator` 和 `@JsonProperty` 注解。 2. **使用 jackson-module-parameter-names**:引入依赖并注册 `ParameterNamesModule` 模块。 3. **使用 Mixins 机制**:创建一个 Mixin 类,使用 `@JsonCreator` 和 `@JsonProperty` 注解
Jackson反序列化不可变类
Go文件操作:掌握Go的文件读写与操作技巧
本文介绍了Go语言的文件操作功能,包括文件的打开、读写和关闭。Go语言通过`os`和`io`包提供了丰富的文件操作接口,使开发者能够轻松实现文件的读写和管理。文章详细讲解了核心概念、具体操作步骤和代码示例,并探讨了实际应用场景和未来发展趋势。
136 4
Spring FactoryBean 的常见使用场景总结
FactoryBean 是 Spring 框架中的一个重要接口,用于自定义 Bean 的创建逻辑。常见使用场景包括: 1. **复杂 Bean 的创建**:如数据源配置。 2. **延迟实例化**:按需创建资源密集型对象。 3. **动态代理**:为 Bean 创建 AOP 代理。 4. **自定义配置**:根据特定配置创建 Bean。 5. **第三方库集成**:利用 FactoryBean 封装外部库的创建过程。
203 0
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
180 1
golang 栈数据结构的实现和应用
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问