工作已有五年之久,回望过去,没有在一线城市快节奏下学习成长,只能自己不断在工作中学习进步,最近一直想写写属于自己的文章,记录学习的内容和知识点,当做一次成长。
摘要:Java泛型是JDK5中引入的一个新特性,其本质是参数化类型。
什么是泛型
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参列表,普通方法的形参列表中,每个形参的数据类型是确定的,而变量是一个参数。在调用普通方法时需要传入对应形参数据类型的变量(实参),若传入的实参与形参定义的数据类型不匹配,则会报错。
在 Java 1.5 之前没有泛型,通常需要使用强制类型转换的方式将一种数据类型转换为另一种数据类型,这种转换要求开发者对实际参数的类型具有可预知性。对于强制类型转换错误的情况,编译器可能不会提示错误,但是在运行时会出现异常,这是一个安全隐患。
为了解决这一隐患,从 Java 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。
泛型本质上是提供类型的“类型参数”,也就是参数化类型。我们可以为类、接口或方法指定一个参数类型,通过这个操作限制数据操作的类型,从而保证类型转换的绝对安全。
代码Map<Integer,Book> books=new HashMap<Integer,Book>();
创建了一个键类型为 Integer
、值类型为 Book
的泛型集合,即指明了该 Map
集合中存放的键必须是 Integer
类型、值必须为 Book
类型,否则编译出错。在获取 Map
集合中的元素时,不需要将books.get(id);
获取的值强制转换为 Book
类型,程序会隐式转换。在创建 List
集合时,同样使用了泛型,因此在获取集合中的元素时也不需要将bookList.get(i)
代码强制转换为 Book
类型,程序会隐式转换。
泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static
的方法而言,无法访问泛型类的类型参数。因此,如果 static
方法需要使用泛型能力,就必须使其成为泛型方法。
代码解读复制代码[访问权限修饰符][static][final]<类型参数列表> 返回值类型 方法名([形式参数列表]){};
代码解读复制代码public static <T, U> void printPair(T first, U second) {
System.out.println(first + ", " + second);
}
extends
关键字对类型参数进行约束,要求类型参数必须是某个类(或接口)的子类(或实现类)。java 代码解读复制代码public static <T extends Number> void printNumber(T num) {
System.out.println(num);
}
泛型接口是Java泛型机制的一部分,它允许在接口定义中使用类型参数,使得实现该接口的类或方法在遵循接口规范的同时,可以处理不同的数据类型。
代码解读复制代码public interface GenericInterface<T> {
void someMethod(T t);
T anotherMethod();
}
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类型的实例
}
}
代码解读复制代码public interface MultipleGenericInterface<T, U> {
T getFirst();
U getSecond();
}
代码解读复制代码public interface MultipleGenericInterface<T extends Number> {
// 省略其他方法
}
泛型类是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;
}
}
java
代码解读复制代码GenericClass<String> stringInstance = new GenericClass<>("Hello");
GenericClass<Integer> integerInstance = new GenericClass<>(123);
代码解读复制代码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;
}
// 省略其他方法
}
泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即编译器会在编译期间擦除代码中的所有泛型语法并相应的做出一些类型转换动作。
换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。也就是说,成功编译过后的 class
文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。
类型参数
,用于指代任何数据类型。 代码解读复制代码 T :代表一般的任何类。
E :代表 Element 元素的意思,或者 Exception 异常的意思。
K :代表 Key 的意思。
V :代表 Value 的意思,通常与 K 一起配合使用。
S :代表 Subtype 的意思,文章后面部分会讲解示意。
在Java的泛型机制中,通配符(wildcard)是一种特殊的类型参数,它用于表示未知的类型。泛型通配符主要用于泛型方法的定义中,以及作为泛型类型和泛型方法之间的桥梁,增加代码的灵活性。以下是关于泛型通配符的关键点:
问号(?) :
问号(?)是泛型通配符的基本符号,它表示未知的类型。在泛型方法中,可以使用问号来代替具体的类型参数。
上限通配符(? extends T) :
上限通配符表示未知的类型是T类型或T类型的子类。它用于限制泛型类型的上界,确保泛型方法或泛型集合中的元素是T类型或其子类类型。
例如,List<? extends Number>
表示一个包含Number
或其子类(如Integer
、Double
等)的列表。
下限通配符(? super T) :
下限通配符表示未知的类型是T类型或T类型的父类。它用于限制泛型类型的下界,允许泛型方法或泛型集合添加T类型或其子类类型的元素,但读取时只能保证是T类型的父类。
例如,List<? super Integer>
表示一个可以包含Integer
或其父类(如Number
、Object
等)的列表,但可以安全地向其中添加Integer
类型的元素。