V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
codergrowing
V2EX  ›  Java

关于 Java 中类型擦除的一些疑惑

  •  1
     
  •   codergrowing · 2019-12-20 14:04:33 +08:00 · 4077 次点击
    这是一个创建于 1793 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题的起因是在我使用 Spring 的BeanUtils.copyProperties方法时发现两个 POJO 如果拥有相同的字段名,即使泛型类型不同,值也会被拷贝过去。例如类 A 有一个类型为List<Integer>的字段 integerList,类 B 有一个类型为List<String>的字段也叫 integerList,那么使用 BeanUtils.copyProperties(b, a)时,integerList 字段会被拷贝过去。这时我想起了 Java 的类型擦除问题。我想问:

    1. 上述问题是因为类型擦除导致的吗
    2. 网上很多文章都是说类型擦除是发生在编译时,即编译后的 class 文件已经没有泛型类型信息了。但 class 文件反编译后的确还是有泛型信息的,如下图。所以类型擦除究竟发生在哪个阶段?

    class.png

    1. 如下的代码中,通过打断点可以看到 method 的 signature 字段是有泛型类型信息的,而这已经是运行时了,为什么泛型类型信息还在?
    public class InvokeDTO {
        private List<Integer> integerList;
    
        public List<Integer> getIntegerList() {
            return integerList;
        }
    
        public void setIntegerList(List<Integer> integerList) {
            this.integerList = integerList;
        }
    }
    
        public static void main(String[] args) {
    
            InvokeDTO invokeDTO = new InvokeDTO();
            Method[] methods = invokeDTO.getClass().getMethods();
    
            for (Method method : methods) {
                try {
                    if (method.getName().equals("setIntegerList")) {
                        method.invoke(invokeDTO, Lists.newArrayList("string0", "string1"));
                    }
                } catch (Exception e) {}
            }
            
            System.out.println(invokeDTO.getIntegerList());
        }
    

    debug.png

    9 条回复    2019-12-21 12:18:36 +08:00
    octobersnow
        1
    octobersnow  
       2019-12-20 14:08:01 +08:00 via iPhone
    我记得类型擦出的确在编译阶段,但是字节码中貌似还是有相关的信息。
    IMCA1024
        2
    IMCA1024  
       2019-12-20 14:08:31 +08:00
    点进去源码不是可以看了吗
    cheng6563
        3
    cheng6563  
       2019-12-20 15:02:49 +08:00 via Android
    局部变量里的泛型运行时没有任何作用。其他地方的泛型可以从反射 API 里读到,可以人工判断,但就算不判断也能塞不同的类型进去。
    iffi
        4
    iffi  
       2019-12-20 15:57:07 +08:00   ❤️ 1
    擦除是防止编译期间 不匹配的数据能写入。Class 记录了泛型类型。运行时是能获取泛型信息的。
    palmers
        5
    palmers  
       2019-12-20 16:01:40 +08:00   ❤️ 2
    泛型擦除的结论是没有问题的, 只不过现在 class 文件确实记录了泛型信息 - Signature Attribute,所以 javap 等反编译工具很多都可以解析到泛型信息, 但是这个泛型信息并不是给 jvm 用的, 具体规范这里有详细的描述:
    https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
    kilen3a
        7
    kilen3a  
       2019-12-20 16:21:50 +08:00   ❤️ 1
    字节码的泛型是反射和 debug 时用的(获取当然也只能通过反射),泛型擦除在编译器发生这个说法没问题,而且运行时 JVM 不会使用到 class 常量池中的泛型
    kilen3a
        8
    kilen3a  
       2019-12-20 16:26:21 +08:00
    chocotan
        9
    chocotan  
       2019-12-21 12:18:36 +08:00
    楼上都说完了,我说个题外话
    ----
    最近运行时拿泛型头都大了
    ( dubbo 泛化调用子属性有泛型就 gg,得手动传 class 进去,我一个消费端,还得服务端告诉我子属性是什么类型?于是在服务端折腾反射拿泛型类型,泛型如果是个 List,List 里面又是个泛型,整个人都蒙圈了。。。我用 spring mvc 没问题,用你 dubbo 就得客户端传类型给你?虽然最后是勉强完成了。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   942 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 21:27 · PVG 05:27 · LAX 13:27 · JFK 16:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.