Java 中的泛型

一、泛型的定义与作用

泛型,又叫 参数多态,在c++里又叫模板。是指声明与定义函数或变量时不指定其具体的类型,而把这部分类型作为参数使用,使得该定义对各种具体类型都适用。通常意义的多态是将这里不特定的类型参数替换成父类型,将具体类型的实现替换成子类型。故而泛型可看作一种特殊的多态(概念角度)。

泛型在强类型的编程语言中普遍作用是:

1. 可以显式地使用参数注入,实现代码复用(避免粗暴地使用 Object)

2. 加强编译时的类型安全(类型检查)

3. 减少类型转换的次数

 

二、Java 中的 泛型

编译时进行类型擦除生成与泛型类同名的原始类,但类型参数都被删去了。类型变量由类型限界代替(通常为 Object)。

 

三、协变

Java 泛型不支持协变,数组是协变的。

注:当我们用型別构造出更复杂的型別,原本型別的子型別性质可能被保持、反转、或忽略。比如 Cat 之于 Animal 与 IEnumerable<Cat> 之于 IEnumerable<Animal>其子型別关系被保持,因此称 IEnumerable<T> 具有协变性质。

 

四、泛型擦除

问题1: 什么时候需要避免泛型擦除?

使用泛型,程序在编译时,类型会完全擦除或保留部分(如果定义了上下限)。

涉及到具体类型的操作时会自动加上强制转换。比如:

1
2
3
4
5
public static void main(String[] args) {
    List<Integer> m = new ArrayList<>();
    m.add(3);
    System.out.println(m.get(0) + 5);
}

 编辑器会替换为:

1
2
3
4
5
public static void main(String[] args) {
    List m = new ArrayList();
    m.add(3);
    System.out.println((Integer)m.get(0) + 5);
}

 

类型擦除还会造成以下编译时错误:

错误 ① “Erasure of method xyz(…) is the same as another method in type Abc”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class App {
    public int process(List<Person> people) {
        for (Person person : people) {
            log.info("Processing person: " + person.toString());
        }
        return person.size();
    }
 
    public int process(List<Employee> employees) {
        for (Employee employee : employees) {
            log.info("Processing employee: " + employee.toString());
        }
        return employees.size();
    }
}

 以上代码由于类型擦除,会被编译成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class App {
    public int process(List people) {
        for (Person person : people) {
            log.info("Processing person: " + person.toString());
        }
        return person.size();
    }
    public int process(List employees) {
        for (Employee employee : employees) {
            log.info("Processing employee: " + employee.toString());
        }
        return employees.size();
    }
}

那么存在的错误显而易见。两个具有相同签名 ( process(List) ) 的方法无法在同一个类中共存,简单的解决方法是修改某一个方法的签名。

 

错误 ② “The method xyz(Foo) in the type Abc is not applicable for the arguments (Foo)”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class App {
    public int processPeople(List<Person> people){
        for (Person person : people) {
            log.info("Processing person: " + person.toString());
        }
        return person.size();
    }
..
}
..
List<Employee>employees;
employees = new ArrayList<>();
employees.add(employee1);
employees.add(employee2);
App app = new App();
// ERROR ON NEXT LINE!
app.processPeople(employees);..

 这个错误的原因是泛型集合不具有协变性。无法强制转换。

 

类型擦除有时还会影响序列化性能(因为不知道类型信息,从而无法针对性地选择序列化方式,因此影响了序列化的性能)。

 

问题2: 如何避免泛型擦除?

第一,使用泛型边界。可以有限地保留类型信息。如:

1
LinkedList<? extends Building>,LInkedList<? super House>

 分别设置了上界和下界。在这种情况下编译器不会将泛型信息完全擦除,比如上述两个List经过编译后分别编程了LinkedList<Building> 和 LinkedList<House>。

举例:

使用泛型边界解决Java中集合泛型不支持协变的问题

定义接口 PvItem

1
2
3
4
5
6
7
8
public interface PvItem {
 
    Long getId();
 
    void setPv(long pv);
 
    void setUv(long uv);
}

错误做法(该方法签名只能接收 List<PvItem> 作为参数)

1
void processItems(String itemType, List<PvItem> items);

正确做法(可接收各种实现了 PvItem 接口的 List 类作为参数)

1
<T extends PvItem> void processItems(String itemType, List<T> items);

 

第二,借助类型推断隐式确定具体参数类型,常用于函数式编程类型推断(需要编程语言支持)。

第三,不直接使用泛型。比如定义一个新的类型,继承或实现原泛型类或接口,并且注入具体的参数类型,则不会发生类型擦除。如:

1
2
3
4
5
6
7
8
9
10
env.fromElements(1, 2, 3)
    .map(i -> new DoubleTuple(i, i))
    .print();
 
public static class DoubleTuple extends Tuple2<Integer, Integer> {
    public DoubleTuple(int f0, int f1) {
        this.f0 = f0;
        this.f1 = f1;
    }
}

 

233

参考


https://www.ibm.com/developerworks/cn/java/java-language-type-erasure/index.html

posted on   Lemo_wd  阅读(219)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示