Java泛型机制和应用
总结
- 泛型解决了参数类型缺少检查造成的问题。
- 泛型可以在类、接口、函数上使用。
- 通配符是为了让
Java
泛型支持范围限定,这样使得泛型的灵活性提升,同时也让通用性设计有了更多的空间。
概述
编译期是指把源码交给编译器编译成计算机可执行文件的过程。运行期是指把编译后的文件交给计算机执行,直到程序结束。在Java
中就是把.java
文件编译成.class
文件,再把编译后的文件交给JVM
加载执行。
泛型又叫“参数化类型”。泛型就是在定义类、接口、方法的时候指定某一种特定类型(碗),让类、接口、方法的使用者来决定具体用哪一种类型的参数(盛的东西)。Java
的泛型是在1.5
引入的,只在编译期做泛型检查,运行期泛型就会消失,我们把这称为“泛型擦除”,最终类型都会变成 Object
。
泛型主要解决的问题:
- 集合对元素类型没有任何限制引发的业务问题。
- 把对象写入集合,在获取对象的时候进行强制类型转换出现问题。
语法规则
使用菱形语法表示泛型,例如 List<String> strList= new ArrayList<>();
。
泛型允许在定义类、接口、方法
时使用类型参数,这个类型形参将在变量声明、创建对象、调用方法时动态得指定。
泛型类
类上定义泛型,作用于类的成员变量与函数,代码实例如下:
public class Apple<T> {
// 使用T类型形参定义实际变量
private T info;
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
public Apple(T info) {
this.info = info;
}
public static void main(String[] args) {
// 创建变量时候指定泛型类型,构造器只能使用对应类型
var code = "06eabe81-5ccc-4002-bffd-f85638af8c76"
Apple<String> a1 = new Apple<>("苹果");
System.out.printf(a1.getInfo());
Apple<Double> a2 = new Apple<>(2.13);
System.out.printf(a2.getInfo()+"");
}
}
泛型接口
接口上定义泛型,作用于函数,代码实例如下:
public interface GenericInterface<T> {
public T get();
public void set(T t);
public T delete(T t);
default T defaultFunction(T t){
return t;
}
}
泛型函数
函数返回类型旁加上泛型,作用于函数,代码实例如下:
public class GenericFunction {
public <T> void function(T t) {
}
public <T> T functionTwo(T t) {
return t;
}
public <T> String functionThree(T t) {
return "";
}
}
通配符
通配符是为了让Java
泛型支持范围限定,这样使得泛型的灵活性提升,同时也让通用性设计有了更多的空间。
<?>
:无界通配符,即类型不确定,任意类型<? extends T>
:上边界通配符,即?
是继承自T
的任意子类型,遵守只读不写<? super T>
:下边界通配符,即?
是T
的任意父类型,遵守只写不读
「 通配符限定的范围是体现在确认“参数化类型”的时候,而不是“参数化类型”填充后 」
/**
* 1.创建泛型为Number的List类,Integer、Double、Long等都是Number的子类
* new ArrayList<>() 等价于 new ArrayList<Number>()
*/
List<Number> numberList = new ArrayList<Number>();
/**
* 2.添加不同子类
*/
numberList.add(1);//添加Integer类型
numberList.add(0.5);//添加Double类型
numberList.add(10000L);//添加Long类型
/**
* 3.创建泛型为Number的List类,Integer、Double、Long等都是Number的子类
* 引用是泛型类别是Number,但具体实现指定的泛型是Integer
*/
List<Number> numberListTwo = new ArrayList<Integer>();//err 异常编译不通过
/**
* 4.创建泛型为Integer的List类,把该对象的引用地址指向泛型为Number的List
*/
List<Integer> integerList = new ArrayList<Integer>();
List<Number> numberListThree = integerList;//err 异常编译不通过
上边界通配符只读不写,下边界通配符只写不读。
<? extends T>
上边界通配符不作为函数入参,只作为函数返回类型,比如List<? extends T>
的使用add函数会编译不通过,get函数则没问题。<? super T>
下边界通配符不作为函数返回类型,只作为函数入参,比如List<? super T>
的add函数正常调用,get函数也没问题,但只会返回Object。
设计原则可以参考 PECS (producer-extends,consumer-super)原则。PECS原则也就是说,如果参数化类型表示一个生产者E,就使用<? extends E>,如果参数化类型表示一个消费者E,则使用<? super E>。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
参考
- 一文通关苦涩难懂的Java泛型 - 程序猿阿星
- 《疯狂Java讲义-第9章 泛型》
- Java泛型的PECS原则
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App