yonutix
yonutix

Reputation: 2439

java access Integer constructor through reflection

I'm having this code. Why doesn't it works?(Working meaning that it display 3) How can I fix it?

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);


    }
}

This is the output:

 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

Thanks!

Upvotes: 8

Views: 3316

Answers (4)

Wael Dahman
Wael Dahman

Reputation: 1

int is a type but Integer is a wrapper (class) that use int as an object with many fonction

Upvotes: -1

Nagappan
Nagappan

Reputation: 356

Generic approach of copying any object to another object. This util class is available in the package - org.apache.commons.lang3.

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

Upvotes: 2

Bohemian
Bohemian

Reputation: 425033

The problem here is the distinction between:

Integer.class
int.class

The constructor for Integer takes an int parameter, not an Integer.

To get your magic method to work, you would need to do a special check of the type, and if it's a wrapper class, actually look for a constructor whose parameter is the corresponding primative type.

AFAIK there's no built in way to get the primatove class from the wrapper class - you could use a map and populate it with the mappings:

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

Then in your method:

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

It's OK to pass the int as an Integer in the parameter value - that at least gets auto unboxed.

Upvotes: 5

BarrySW19
BarrySW19

Reputation: 3809

To really understand why "new Integer(new Integer(5))" works while reflections doesn't it's useful to look at the generated byte code for the first case:

ICONST_5
INVOKESPECIAL java/lang/Integer.<init> (I)V
INVOKEVIRTUAL java/lang/Integer.intValue ()I
INVOKESPECIAL java/lang/Integer.<init> (I)V

... as you can see, a call is being made to the intValue() method of Integer "under the hood". So, the java compiler is actually translating your "new Integer(new Integer(5))" to "new Integer(new Integer(5).intValue())". This means it can use the constructor which takes an int.

Because the java compiler cannot know the actual runtime type of the variable for the reflections call it cannot do anything similar and can only look for a constructor with the actual runtime type argument.

Upvotes: 1

Related Questions