Java泛型详情与案例
什么是泛型?
泛型(Generics)是程序设计语言的一种特性,它允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,这些部分在使用前必须作出指明。
而在Java中,泛型是Java SE 5引入的新特性,它允许在定义类、接口和方法时使用类型参数。类型参数在使用前必须被实际类型(如类、接口或基本类型)所替换。
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
通俗一点解释泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
举个例子:
在ArrayList中的Integer称为实际类型参数,而
ArrayList中的E称为类型参数变量,可以提高代码的可读性,让程序员从编译前在添加类型。
E不属于其他类型,他只是一种站位符号,当然你换成T也是可以的。
举个泛型的例子:
// 生成一个泛型类
public class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
// 当你创建一个泛型类或接口的实例,或者调用一个泛型方法时,你需要为类型参数提供一个具体的类型(如String、Integer等)。这个过程被称为类型参数的实例化。例如:
Box<String> stringBox = new Box<String>();
stringBox.set("Hello");
String value = stringBox.get();
在这里,String是T的具体类型,编译器会记住这个类型信息。
Java在编译期做了什么?
泛型(Generics)的类型是在编译期确定的,这主要得益于Java的类型擦除(Type Erasure)机制。
在编译阶段,Java编译器会将泛型信息擦除,并将使用泛型的地方替换为原生类型或者Object。这意味着泛型类型信息不会保留在运行时。
尽管泛型信息在运行时被擦除,但Java的集合框架(如ArrayList、HashSet等)仍然能够在运行时执行类型检查。这是通过保留在泛型类和接口中定义的get()、set()等方法的类型参数信息来实现的。如果试图将错误类型的对象添加到集合中,或者从集合中取出错误类型的对象,那么会在运行时抛出ClassCastException。
让我们举个例子:
import java.util.ArrayList;
import java.util.List;
public class TypeErasureExample {
public static void main(String[] args) {
// 创建一个Integer类型的ArrayList
List<Integer> integerList = new ArrayList<>();
// 添加一个Integer到列表中
integerList.add(10);
// 尝试添加一个String到列表中(编译时错误)
// integerList.add("This is a string"); // 这行代码会导致编译错误
// 由于类型擦除,下面的代码在编译时不会报错,但在运行时可能会抛出ClassCastException
List rawList = integerList; // raw type,即原生类型
rawList.add("This is a string"); // 编译时不会报错,因为raw type不知道其元素的具体类型
// 尝试从列表中获取一个Integer对象,但实际上是String,这将在运行时抛出ClassCastException
Integer number = integerList.get(1); // 这里会抛出ClassCastException,因为index 1 的元素实际上是String
System.out.println(number);
}
}
/*类中List<Integer>就是泛型的具体类型,如果List也是可以的。
* 但是没有声明具体类型的话,在运行期间可能会获取不符合对象类型,从而抛出异常
* /
为什么需要泛型?
早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全。
有了泛型以后:
-
代码更加简洁【不用强制转换】
-
程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
-
可读性和稳定性【在编写集合的时候,就限定了类型】
总结
如有不足的地方欢迎大佬们在评论区指出。也还望各位觉得有用的话一键三连+关注,谢谢!