8

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!

10
  • 5
    Integer only has int or String argument constructors, no Integer copy constructor. With reflection, you cannot unbox the parameters automatically. Commented Jan 7, 2015 at 13:43
  • Then why does this works Integer c = new Integer(new Integer(5)); Commented Jan 7, 2015 at 13:45
  • You are making a lot of assumptions in a generic method here. Not all classes are going to have a constructor that takes a value of the same class. Commented Jan 7, 2015 at 13:45
  • 2
    @CosminMihai That's because auto unboxing happens from Integer to int automatically there.. Commented Jan 7, 2015 at 13:46
  • 1
    More importantly: Why do you want to copy an instance of an immutable class? What do you gain if, you have another boxed Integer with the same int value? Commented Jan 7, 2015 at 14:02

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Ok, I understand, thanks. If you have any idea how I could dodge that keeping the purpose of the method generic please share.
@CosminMihai See code for how to handle elegantly. Merry Xmas :)
Thank you very much, hope that the corresponding class for char primitive type is Character
@CosminMihai yes, it is. All wrappers except Integer and Character are the same name as the primative but capitalised.
2

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

2 Comments

SerializationUtils is not in the standard library, I was looking for something nice.
Using a mature library, that is already used by many users and big projects is much nicer, than putting something together in a few hours by yourself. Just my opinion.
1

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.

1 Comment

I think i will put some if statements or a switch for Integer, Float, Double and Long classes to resolve those particular cases
-1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.