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的旧版本(即不支持泛型的版本)兼容。注意,泛型信息只存在于源代码和编译时,在运行时,所有的泛型类型信息都会被擦除。这意味着在运行时,所有泛型类型都被替换为它们的上限类型(如果没有指定上限,则默认为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也指定了泛型会怎么样呢?
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
那么编译通过了,运行会不会报错呢?答案在我们这个例子中,是不会报错的。但记住主要原因是我们把方法返回的pageInfo直接以json字符串的形式返回给前端了,中间并没有做类型强转的操作。假如我们把PageInfo中的list做强转,必然会报类型转换异常。
我们来分析一下具体的原因:结合泛型擦除的概念,运行时,PageInfo
但如果你已经了解泛型的话,上面的代码显然是一种冒险,假如有另一个方法调用者,并且通过强转来获取其中的集合的话,这必然会出现异常。
我们来举个简单的例子:
List rawList = new ArrayList(); // 允许,但不安全
rawList.add(123); // 编译时不报错,但运行时可能导致问题
String str = (String)rawList.get(0); // 运行时抛出ClassCastException
本文暂时没有评论,来添加一个吧(●'◡'●)