专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java基础-泛型擦除(泛型擦除如何获取类型)

temp10 2025-03-25 17:24:00 java教程 12 ℃ 0 评论

PageInfo这个类大家应该很熟悉了,那么我们看下面一段代码,该方法的调用者会直接将参数返回前端,假如中间逻辑处理没有问题,代码可以编译通过并成功运行吗?

//AgentInfoVo和AgentInfosRsp中的参数完全不同,实际我们想要的也是返回AgentInfosRsp
//但方法返回的是泛型AgentInfoVo
public PageInfo queryAgentInfoVo(String companyNum, String agentName, String agentId, String currentPage, String pageSize) {
    AgentInfoVo agentInfoVo = new AgentInfoVo();
    List agentInfosRspList = new ArrayList<>();
    ...
		...
    Page page = new Page(pageNum, size);
    //为Page类中的total属性赋值
    int total = agentInfosRspList.size();
   	page.setTotal(total);
    //计算当前需要显示的数据下标起始值
    int startIndex = (pageNum - 1) * size;
    int endIndex = Math.min(startIndex + size,total);
    //从链表中截取需要显示的子链表,并加入到Page
  	//这个page实际添加的是泛型AgentInfosRsp
    page.addAll(agentInfosRspList.subList(startIndex,endIndex));
  	//page.getResult()实际是List
  	//强转成了List并返回
    return new PageInfo((List)page.getResult());
}

实际上,这段代码是可以编译通过并运行的且能达到目的。

Java基础-泛型擦除(泛型擦除如何获取类型)

思考一下,为什么代码可以成功运行呢?

这里涉及到一个基础概念:泛型擦除。

泛型擦除是Java编译器在编译泛型代码时的一种机制,它的目的是确保泛型能够与JAVA的旧版本(即不支持泛型的版本)兼容。注意,泛型信息只存在于源代码和编译时,在运行时,所有的泛型类型信息都会被擦除。这意味着在运行时,所有泛型类型都被替换为它们的上限类型(如果没有指定上限,则默认为Object)。

关键:泛型信息只存在于源代码和编译,运行时,所有的泛型类型信息都会被擦除。

再次回到上面的代码中,泛型擦除可以理解为是为了兼容旧版本,因此如果返回的PageInfo没有泛型类型的话,编译器是检查不出来的。所以这也是上面的代码可以编译通过的原因了。

我们不妨再多想一下,假如我们给最终返回的PageInfo加上泛型会怎么样呢?

public PageInfo queryAgentInfoVo(String companyNum, String agentName, String agentId, String currentPage, String pageSize) {
    AgentInfoVo agentInfoVo = new AgentInfoVo();
    List agentInfosRspList = new ArrayList<>();
    ...
		...
    Page page = new Page(pageNum, size);
    //为Page类中的total属性赋值
    int total = agentInfosRspList.size();
   	page.setTotal(total);
    //计算当前需要显示的数据下标起始值
    int startIndex = (pageNum - 1) * size;
    int endIndex = Math.min(startIndex + size,total);
    //从链表中截取需要显示的子链表,并加入到Page
  	//这个page实际添加的是泛型AgentInfosRsp
    page.addAll(agentInfosRspList.subList(startIndex,endIndex));
  	//page.getResult()实际是List
    return new PageInfo(page.getResult());
}

假如我们加的泛型是AgentInfoVo的话,编译也会通过,因为最终返回的泛型类型与方法要求返回的泛型类型一致。此时,PageInfo的构造方法要求的是一个List,而page.getResult()由于此时没有指定Page的泛型,因此编译器是检查通过的(兼容旧版本)。

那么如果我们给Page也指定了泛型会怎么样呢?

public PageInfo queryAgentInfoVo(String companyNum, String agentName, String agentId, String currentPage, String pageSize) {
    AgentInfoVo agentInfoVo = new AgentInfoVo();
    List agentInfosRspList = new ArrayList<>();
    ...
		...
    Page page = new Page<>(pageNum, size);
    //为Page类中的total属性赋值
    int total = agentInfosRspList.size();
   	page.setTotal(total);
    //计算当前需要显示的数据下标起始值
    int startIndex = (pageNum - 1) * size;
    int endIndex = Math.min(startIndex + size,total);
    //从链表中截取需要显示的子链表,并加入到Page
  	//这个page实际添加的是泛型AgentInfosRsp
    page.addAll(agentInfosRspList.subList(startIndex,endIndex));
  	//page.getResult()实际是List
    return new PageInfo(page.getResult());
}

此时,编译器必然会报错,因为PageInfo构造方法要求的参数是List,而page.getResult()返回的是List

那么编译通过了,运行会不会报错呢?答案在我们这个例子中,是不会报错的。但记住主要原因是我们把方法返回的pageInfo直接以json字符串的形式返回给前端了,中间并没有做类型强转的操作。假如我们把PageInfo中的list做强转,必然会报类型转换异常。

我们来分析一下具体的原因:结合泛型擦除的概念,运行时,PageInfo会变为PageInfo<Object>,而PageInfo中的list也会变更List<Object>,因此,即便我们知道pageInfo中的list中存储的是AgentInfosRsp对象,由于没有做任何类型转化的操作,而是直接返回到前端,因此在返回前端进行序列化时,并不会出现问题,而且也可以把pageInfo中存储AgentInfosRsp集合以json字符串的形式展示给前端。

但如果你已经了解泛型的话,上面的代码显然是一种冒险,假如有另一个方法调用者,并且通过强转来获取其中的集合的话,这必然会出现异常。

我们来举个简单的例子:

List rawList = new ArrayList(); // 允许,但不安全
rawList.add(123); // 编译时不报错,但运行时可能导致问题
String str = (String)rawList.get(0); // 运行时抛出ClassCastException

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表