通过反射访问Java Integer构造函数

8

我有这段代码。为什么它不能工作?(“工作”意味着它应该显示3)。我该怎么修复它?

public class Main {
    public static<V> V copy(V var){
        try{ 
            return (V) var.getClass().getConstructor(var.getClass()).newInstance(var);
        }
        catch(Exception e){
            System.out.println("Copy faield " + e.getMessage() + " ");
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = copy(a);

        System.out.println(a);
        System.out.println(b);


    }
}

这是输出结果:
 Copy faield java.lang.Integer.<init>(java.lang.Integer) 
    java.lang.NoSuchMethodException: java.lang.Integer.<init>(java.lang.Integer)
        at java.lang.Class.getConstructor0(Class.java:2818)
        at java.lang.Class.getConstructor(Class.java:1723)
        at Main.copy(Main.java:7)
        at Main.main(Main.java:19)
    3
    null

谢谢!


5
Integer 只有 intString 类型的参数构造函数,没有 Integer 类型的拷贝构造函数。使用反射时,无法自动拆箱参数。 - Gábor Bakos
您在这个通用方法中做了很多假设。并非所有类都会有一个接受相同类的值的构造函数。 - mikea
2
@CosminMihai 这是因为在这里自动将 Integer 解封到 int 类型。 - SMA
@CosminMihai 它能工作是因为 javac 在那里执行了一个拆箱操作。但是在反射期间,您没有传递 intClass,而是传递了 Integer 的,因此它无法在运行时执行该操作。 - Gábor Bakos
1
更重要的是:为什么要复制一个不可变类的实例?如果你有另一个具有相同int值的装箱Integer,你会得到什么好处? - Balázs Édes
显示剩余5条评论
4个回答

5
这里的问题在于区分以下两点:
Integer.class
int.class

Integer的构造函数接受一个int参数,而不是Integer。

为了使您的神奇方法起作用,您需要对类型进行特殊检查,如果它是包装类,则实际上要查找其参数为相应基本类型的构造函数。

据我所知,没有内置的方法可以从包装类中获取原始类 - 您可以使用映射并填充它们的映射关系:

private static final Map<Class<?>, Class<?>> MAP = new HashMap<>() {{
    put(Integer.class, int.class);
    put(Long.class, long.class);
    // etc
}};

然后,在你的方法中:

Class<?> type = MAP.containsKey(var.getClass()) ? MAP.get(var.getClass()) : var.getClass();
return (V) var.getClass().getConstructor(type).newInstance(var);

在参数值中将int传递为Integer是可以的 - 至少能自动进行拆箱。


好的,我明白了,谢谢。如果您有任何想法,如何避免这种情况并保持方法的通用性,请分享。 - yonutix
@CosminMihai 请查看代码以了解如何优雅地处理。圣诞快乐 :) - Bohemian
非常感谢,希望 char 原始类型的相应类是 Character。 - yonutix
1
@CosminMihai 是的,就是这样。除了 Integer 和 Character 之外,所有的包装类都与原始类型同名但首字母大写。 - Bohemian

2

将任何对象复制到另一个对象的通用方法。这个工具类可以在org.apache.commons.lang3包中找到。

 Integer c = (Integer) SerializationUtils.clone(a);

SerializationUtils不在标准库中,我正在寻找一些好用的东西。 - yonutix
使用一个成熟的库,已经被许多用户和大型项目使用,比自己在几个小时内组合一些东西要好得多。这只是我的观点。 - Balázs Édes

1
要真正理解为什么"new Integer(new Integer(5))"可以工作而反射不行,看一下第一个案例生成的字节码是很有用的:
ICONST_5
INVOKESPECIAL java/lang/Integer.<init> (I)V
INVOKEVIRTUAL java/lang/Integer.intValue ()I
INVOKESPECIAL java/lang/Integer.<init> (I)V

正如您所看到的,“底层”正在调用Integer的intValue()方法。 因此,Java编译器实际上将您的“new Integer(new Integer(5))”翻译为“new Integer(new Integer(5).intValue())”。 这意味着它可以使用接受int参数的构造函数。
由于Java编译器无法知道反射调用的变量的实际运行时类型,因此它不能执行类似的操作,只能查找具有实际运行时类型参数的构造函数。

我认为我会为整数、浮点数、双精度和长整型类放置一些if语句或开关来解决这些特定情况。 - yonutix

-1

int是一种类型,但Integer是一个使用int作为对象并具有许多功能的包装器(类)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接