Java 泛型 (Generics)

泛型是 Java 5 引入的重要特性,它允许在定义类、接口和方法时使用类型参数(type parameters),从而使代码可以适用于多种类型,同时提供编译时类型安全检查。

泛型的基本概念

  1. 泛型类
java 复制代码
public class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

// 使用

java 复制代码
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String value = stringBox.getContent(); // 无需类型转换
  1. 泛型接口
    java
    public interface Pair<K, V> {
    K getKey();
    V getValue();
    }
  2. 泛型方法
    java
    public class Util {
    public static T getMiddle(T[] array) {
    return array[array.length / 2];
    }
    }

// 使用

String[] names = {"Alice", "Bob", "Charlie"};

String middle = Util.getMiddle(names); // 类型推断可省略

泛型的类型参数命名约定

E - 元素 (Element),用于集合框架

K - 键 (Key)

V - 值 (Value)

N - 数字 (Number)

T - 类型 (Type)

S, U, V 等 - 第二、第三、第四类型

类型通配符

  1. 无界通配符 (?)
    java
    public void printList(List<?> list) {
    for (Object elem : list) {
    System.out.println(elem);
    }
    }
  2. 上界通配符 (? extends Type)
    java
    public double sumOfList(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
    sum += num.doubleValue();
    }
    return sum;
    }
  3. 下界通配符 (? super Type)
    java
    public void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 5; i++) {
    list.add(i);
    }
    }
    类型擦除
    Java 泛型是通过类型擦除实现的,这意味着泛型信息只在编译时存在,运行时会被擦除:

泛型类型参数会被替换为它们的边界或 Object

必要时插入类型转换

桥方法被生成以保持多态

java

// 编译前

List list = new ArrayList<>();

// 编译后(类型擦除)

List list = new ArrayList();

泛型的限制

不能使用基本类型作为类型参数:

java

List // 错误,必须使用 List

不能创建泛型数组:

java

T[] array = new T[10]; // 错误

不能实例化类型参数:

java

public T create() {

return new T(); // 错误

}

不能在静态上下文中使用类的类型参数:

java

public class Foo {

private static T value; // 错误

}

不能抛出或捕获泛型类的实例:

java

try {

// ...

} catch (T ex) { // 错误

// ...

}

泛型与继承

泛型类即使类型参数有继承关系,它们之间也没有继承关系:

java

List 不是 List 的子类

通配符与继承

java

List<? extends Number> 可以接受 List 或 List

List<? super Integer> 可以接受 List 或 List 或 List

PECS 原则 (Producer-Extends, Consumer-Super)

Producer (生产者,提供数据): 使用 extends

Consumer (消费者,接收数据): 使用 super

java

// 生产者示例

public static void copy(List<? extends T> source, List<? super T> destination) {

for (T item : source) {

destination.add(item);

}

}

范型方法特别说明:

在 Java 泛型方法中,如果参数是泛型类型(如 T),而该方法需要处理具体类型(如 String 和 Integer)的特定逻辑,可以通过以下几种方式实现:

方法 1:使用 instanceof 进行类型检查

在泛型方法内部,可以通过 instanceof 检查泛型参数的具体类型,然后执行不同的逻辑。

示例代码

java

public class GenericMethodExample {

复制代码
// 泛型方法
public static <T> void process(T input) {
    if (input instanceof String) {
        // 处理 String 类型
        String str = (String) input;
        System.out.println("String: " + str.toUpperCase());
    } else if (input instanceof Integer) {
        // 处理 Integer 类型
        Integer num = (Integer) input;
        System.out.println("Integer: " + (num * 2));
    } else {
        // 其他类型
        System.out.println("Unknown type: " + input);
    }
}

public static void main(String[] args) {
    process("hello");  // 输出: String: HELLO
    process(123);      // 输出: Integer: 246
    process(3.14);     // 输出: Unknown type: 3.14
}

}

说明

instanceof 检查泛型参数的具体类型。

需要手动强制类型转换((String) input)。

适用于少量已知类型的情况,但如果类型很多,代码会变得冗长。

方法 2:方法重载(推荐)

如果逻辑差异较大,可以直接定义多个重载方法,而不是使用泛型方法。

示例代码

java

public class OverloadExample {

复制代码
// 处理 String
public static void process(String input) {
    System.out.println("String: " + input.toUpperCase());
}

// 处理 Integer
public static void process(Integer input) {
    System.out.println("Integer: " + (input * 2));
}

// 处理其他类型
public static <T> void process(T input) {
    System.out.println("Unknown type: " + input);
}

public static void main(String[] args) {
    process("hello");  // 调用 String 版本
    process(123);      // 调用 Integer 版本
    process(3.14);     // 调用泛型版本
}

}

说明

更符合 Java 的类型安全机制。

避免了 instanceof 和强制类型转换。

代码更清晰,推荐使用。

方法 3:使用 Class 参数(反射)

如果需要动态处理类型,可以传入 Class 参数,结合反射进行操作。

示例代码

java

public class ClassParamExample {

复制代码
public static <T> void process(T input, Class<T> type) {
    if (type == String.class) {
        String str = (String) input;
        System.out.println("String: " + str.toUpperCase());
    } else if (type == Integer.class) {
        Integer num = (Integer) input;
        System.out.println("Integer: " + (num * 2));
    } else {
        System.out.println("Unknown type: " + input);
    }
}

public static void main(String[] args) {
    process("hello", String.class);  // 输出: String: HELLO
    process(123, Integer.class);     // 输出: Integer: 246
    process(3.14, Double.class);     // 输出: Unknown type: 3.14
}

}

说明

适用于需要动态类型检查的场景。

仍然需要强制类型转换,但避免了 instanceof 的冗余检查。

方法 4:使用策略模式(面向对象)

如果逻辑复杂,可以使用策略模式,将不同类型的处理逻辑封装成不同的类。

示例代码

java

interface Processor {

void process(T input);

}

class StringProcessor implements Processor {

@Override

public void process(String input) {

System.out.println("String: " + input.toUpperCase());

}

}

class IntegerProcessor implements Processor {

@Override

public void process(Integer input) {

System.out.println("Integer: " + (input * 2));

}

}

public class StrategyPatternExample {

复制代码
public static <T> void process(T input, Processor<? super T> processor) {
    processor.process(input);
}

public static void main(String[] args) {
    process("hello", new StringProcessor());  // 输出: String: HELLO
    process(123, new IntegerProcessor());     // 输出: Integer: 246
}

}

说明

适用于复杂逻辑,符合开闭原则(OCP)。

扩展性强,但代码量较多。

推荐方案

如果类型较少且明确 → 方法重载(最简洁、最安全)。

如果需要动态处理 → Class 参数 或 策略模式。

避免滥用 instanceof,除非逻辑非常简单。

希望这些方法能帮助你灵活处理泛型参数! 🚀

总结

Java 泛型提供了编译时类型安全,减少了运行时类型转换错误,使代码更清晰、更安全。理解类型擦除、通配符和 PECS 原则对于有效使用泛型至关重要。

相关推荐
你怎么知道我是队长4 分钟前
python-enumrate函数
开发语言·chrome·python
小屁孩大帅-杨一凡16 分钟前
如何解决ThreadLocal内存泄漏问题?
java·开发语言·jvm·算法
大熋21 分钟前
Playwright Python 教程:网页自动化
开发语言·python·自动化
小白爱电脑21 分钟前
电脑上如何查看WiFi密码
windows·电脑
学习3人组37 分钟前
在 IntelliJ IDEA 系列中phpstorm2025设置中文界面
java·ide·intellij-idea
赟赟、嵌入式41 分钟前
imx6ul Qt运行qml报错This plugin does not support createPlatformOpenGLContext!
开发语言·qt
cdg==吃蛋糕1 小时前
selenium 使用方法
开发语言·python
爱掉发的小李2 小时前
前端开发中的输出问题
开发语言·前端·javascript
zyx没烦恼2 小时前
五种IO模型
开发语言·c++
cainiao0806052 小时前
Java 大视界:基于 Java 的大数据可视化在智慧城市能源消耗动态监测与优化决策中的应用(2025 实战全景)
java