Reputation: 2246
UPDATE - To make the question clearer.
What is the possible cause of getting a ClassCastException while calling a method via reflections?
I got the following stacktrace as a part of my application while trying to invoke a method via reflections.
java.lang.IllegalArgumentException: java.lang.ClassCastException@21fea1fv
at sun.reflect.GeneratedMethodAccessor332.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.....
(remaining is my method stack trace)
I tried an example class and passed various arguments of different types to it, but i always get a this exception.
java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
UPDATE - Here is the sample code i wrote to try recreating the exception
Interface to create proxy class
package my.tests;
public interface ReflectionsInterface {
public abstract void doSomething();
}
This is the test class
package my.tests;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Reflections implements ReflectionsInterface {
public static void main(String[] args) {
Reflections reflections = new Reflections();
ReflectionsInterface reflectionsProxy = reflections.createProxy(ReflectionsInterface.class);
invokeMethod(reflectionsProxy, "doSomething", null);
}
public <T> T createProxy(Class<T> entityInterface) {
EntityInvocationHandler eih = new EntityInvocationHandler(this);
T cast = entityInterface.cast(Proxy.newProxyInstance(
entityInterface.getClassLoader(), new Class[]{entityInterface}, eih));
return cast;
}
public static void invokeMethod(Object obj, String methodName, Object... args) {
Method[] methods = obj.getClass().getMethods();
try {
for (Method method : methods) {
if (method.getName().equals(methodName)) {
method.invoke(obj, args);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void doSomething() {
System.out.println("woo");
}
private final static class EntityInvocationHandler implements InvocationHandler,
ReflectionsInterface {
private Reflections reflectionObj;
public EntityInvocationHandler(Reflections reflectionObj) {
super();
this.reflectionObj = reflectionObj;
}
@Override
public void doSomething() {
reflectionObj.doSomething();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(this, args);
return invoke;
}
}
}
I am unable to understand the when i would get argument type mismatch and the ClassCastException would be caused. I am not able to re-create the exception and would like to know why it comes. Any working code that re-created it, or a source code reference throwing this exception in this case will be good
I have gone through the Method.class javadocs and source code, i am not able to figure out why this error comes.
Upvotes: 11
Views: 30611
Reputation: 9599
I had recreated the ClassCastException
by modifing your example code: Invoke invokeMethod
with a correct argument 10000 times, and then invoke it with wrong a wrong one.
The main
method in the Reflections
class
public static void main(String[] args) {
Reflections reflections = new Reflections();
ReflectionsInterface reflectionsProxy = reflections
.createProxy(ReflectionsInterface.class);
for (int i = 0; i < 10000; i++)
invokeMethod(reflectionsProxy, ReflectionsInterface.class,
"doSomething");
invokeMethod(new Object(), ReflectionsInterface.class, "doSomething");
}
The invokeMethod
method in the Reflections
class
public static void invokeMethod(Object obj, Class<?> clazz,
String methodName, Object... args) {
Method[] methods = clazz.getMethods();
try {
for (Method method : methods) {
if (method.getName().equals(methodName)) {
method.invoke(obj, args);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Stack Trace:
java.lang.IllegalArgumentException: java.lang.ClassCastException@603a3e21
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.twbbs.pccprogram.Reflections.invokeMethod(Reflections.java:33)
at org.twbbs.pccprogram.Reflections.main(Reflections.java:16)
My explanation of the ClassCastException
:
When you invoke invokeMethod
for the first time, JVM use a slower route, which is easier for programmers to debug (so it's slower!), so it will show a more friendly argument type mismatch
message when you passes a bad argument.
When you invoke invokeMethod
for a lot of times (16 times are enough in my tests), JVM generated a GeneratedMethodAccessor***
in runtime, which is faster, with less error checking. So it will show such an ugly java.lang.ClassCastException@603a3e21
message when you passes a bad argument.
Upvotes: 16
Reputation: 635
I have a bit modify your code to reproduce this error :
java.lang.IllegalArgumentException: argument type mismatch
Here is the interface
public interface ReflectionsInterface
{
public abstract void doSomething1(Integer i);
public abstract void doSomething2(String s);
}
Add the method in class Reflections and EntityInvocationHandler in order to compile. Then add this method in your main(). Execute and I produce the error runtime.
invokeMethod(reflectionsProxy, "doSomething1", "1");
I have tried to get un ClassCastException but I didn't succeed. It occurs probably in your original code because there are more objects and an invalid type is passed to the method. Add logs and try to debug to isolated the ClassCastException.
Upvotes: 0
Reputation: 1500785
Well this is the problem:
reflections.invokeMethod("doInt", 1L);
You're calling doInt
, but you're passing a long
value. So reflection is trying to cast a Long
to an Integer
, which is invalid.
I suspect you meant:
reflections.invokeMethod("doLong", 1L);
Upvotes: 1