Reputation: 2439
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
Reputation: 1
int is a type but Integer is a wrapper (class) that use int as an object with many fonction
Upvotes: -1
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
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
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