Java泛型是什么?

程序浅谈 后端 2024-10-28

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 方法中形参类型不确定时
    • 使用类后面定义的泛型。
    • 在方法上定义自己的泛型。

Java泛型是什么?

  • 4.2 格式ini
代码解读
复制代码
[访问权限修饰符][static][final]<类型参数列表> 返回值类型 方法名([形式参数列表]){};

Java泛型是什么?

  • 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泛型是什么?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泛型是什么?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

Apipost 私有化火热进行中

评论