从一段java泛型代码发现java编译器的缺陷

Posted seamas

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从一段java泛型代码发现java编译器的缺陷相关的知识,希望对你有一定的参考价值。

最近在写java代码的时候,需要进行对象转换,由于字段名存在不同,BeanUtils无法满足需求,所以想到了java世界有没有类似C#的AutoMapper库,找到了 ModelMapper

以官方的Getting Started为例 

源对象

// Assume getters and setters on each class
class Order {
  Customer customer;
  Address billingAddress;
}

class Customer {
  Name name;
}

class Name {
  String firstName;
  String lastName;
}

class Address {
  String street;
  String city;
}

  目标对象

// Assume getters and setters
class OrderDTO {
  String customerFirstName;
  String customerLastName;
  String billingStreet;
  String billingCity;
}

  由于以上对象的定义符合约定的规则,可以不需要任何配置,完成类型的转换

ModelMapper modelMapper = new ModelMapper();
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);

  

     重点来了,有时候我们需要转换的是整个List,总不至于让我们对列表中每个对象逐个进行转换,幸好,官方也提供了相应的api

ModelMapper modelMapper = new ModelMapper();
modelMapper.createTypeMap(Order.class, OrderDTO.class);
List<Order> orderList = new ArrayList<>();
//TODO: orderList添加元素
Type listType = new TypeToken<List<OrderDTO>>(){}.getType();
List<OrderDTO> dtoList = modelMapper.map(orderList, listType);

  看起来好像一切正常,程序运行出来的结果也是正确的,但是,细看最后一行代码

 

List<OrderDTO> dtoList = modelMapper.map(orderList, listType);

 

  modelMapper.map方法接收的2个参数类型,一个是 List<Order>, 另外一个是 Type, 编译器无法在编译时刻知道Type中包含具体什么类型,它是怎么知道这个方法返回的类型是 List<OrderDTO>?于是我将这一行代码改成了

List<Order> dtoList = modelMapper.map(orderList, listType);
编译器没有警告和错误提示,运行时查看dtoList中对象的结构为OrderDTO,查看ModelMapper的源码,发现
public class ModelMapper {

  public <D> D map(Object source, Type destinationType){...}
从方法的签名上看,D的类型是无法确定的(虽然方法内部可以通过读取Type中包含的类型,构造出对象),也就是说,唯一能够确定的是方法返回的是Object,通过IDEA的智能提示,这个方法确实返回的是Object, 那么为什么将Object赋值给List对象的时候,编译器没有警告呢?


我们用java和C#对这种泛型写法做一个简单的对比测试
java代码
public class Mapper {

    public <D> D convert(Object obj) {
        return (D)obj;
    }
}
C#代码
public class Mapper
{
    public T convert(Object obj){
        return (T)obj;
    }
}

对java代码进行编译,顺利通过,相反,对C#的代码进行编译,未通过,有错误提示,“Error CS0246: The type or namespace name ‘T‘ could not be found (are you missing a using directive or an assembly reference?) (CS0246) ”,明确指出T的类型未找到,
从我个人的理解,java中的泛型由于运行时的类型擦除,只在编译时起到类型检查的作用,但是上述的写法做到了让编译器的类型检查失效,根本就不应该让这样的代码通过编译,编译器的语义分析存在缺陷。

另外吐槽一下java的类型书写,C#通过AutoMapper对列表的转换可以写成
List<OrderDTO> dtoList = mapper.map<List<OrderDTO>>(orderList);
但是java中没有List<OrderDTO>.class的写法,是很膈应的事情

由于刚从C#入java,理解会有偏差,欢迎指正

 








以上是关于从一段java泛型代码发现java编译器的缺陷的主要内容,如果未能解决你的问题,请参考以下文章

Java泛型学习

JAVA泛型的基本使用

Java 泛型接口转换

[ Java面试题 ]泛型篇

java 方法中如何在返回类型使用泛型

java中的泛型