10

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.

Aditya
  • 2,008
  • 3
  • 18
  • 32
  • The recent edits you made to your code have eliminated the exception that was being thrown. Are you saying you are still getting errors even with this new SSCCE? If not, please revert back to the previous code that actually illustrates the issue you are having. – Perception Apr 19 '13 at 17:22
  • The error is coming from my application code, which is very elaborate and i cannot post here. This is my (failed) attempt to re-create the error or understand why it is coming.. – Aditya Apr 20 '13 at 04:57

3 Answers3

16

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.

johnchen902
  • 9,200
  • 1
  • 25
  • 63
  • 1
    This is quite interesting, i will try this, but the method i'm trying to invoke has no arguments! Any idea why the exception might come in a zero argument method? – Aditya Apr 22 '13 at 13:37
  • @gap_j I had edited my answer to throw the exception in a zero argument method. – johnchen902 Apr 22 '13 at 13:48
  • the error is being recreated in your old case, with arguments. But with the modification (zero argument method), its not being recreated. – Aditya Apr 22 '13 at 14:04
  • It recreated on my computer! Let me check what I forget to post. Uh... Probably you didn't roll `ReflectionsInterface` and `doSomething` back to your original example code version? – johnchen902 Apr 22 '13 at 14:07
  • I have it at the old version. and passing a *new Object()* to *invokeMethod* won't do anything as it won't find a method with a matching name. Are you sure this is the exact code you are using? Could you post the exact code here? Can you also specify which java version you are using. – Aditya Apr 22 '13 at 14:13
  • 1
    @gap_j You need be modify both `main` and `invokeMethod`. – johnchen902 Apr 22 '13 at 14:14
  • 1
    gg John. I searched also a bit on this problem and I couldn't figure how to get this exception. Thx for the question and the answer. +1 for your answer ;) – mki Apr 23 '13 at 18:22
  • 1
    Can someone explain why IllegalArgumentException is being thrown for a call to a method that doesn't take any arguments? – GreenGiant Oct 10 '14 at 21:49
  • 1
    Is there a way to disable this optimization? – Knox Sep 06 '18 at 14:26
1

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);
Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • This is not my application code. I am trying to re-create the classcast exception, so i purposely called a reflections.invokeMethod("doInt", 1L); The error that it throws here is argument type mismatch, not classcast exception – Aditya Apr 17 '13 at 01:46
  • @gap_j: Ah, I see. That wasn't at *all* clear before. How close *is* this code to your actual application code though (in the reflection part)? – Jon Skeet Apr 17 '13 at 05:43
  • 1
    There are 2 differences between this and my application code. 1) the method being invoked in my application has no arguments. 2) It might be being invoked on a proxy class. I am trying to recreate using both these cases, still unsuccessful – Aditya Apr 17 '13 at 07:29
  • @gap_j: If you're invoking a method with no argument, then I can't see that the sample code you provided is helpful at all - it *clearly* can't be an argument conversion issue if there are no arguments! It sounds much more likely that the proxy class difference is the important one. It's really important that a question gives as much context as possible: both of those points should have been in the question from the start. – Jon Skeet Apr 17 '13 at 09:01
  • i did not post the whole code as i thought it'll be a little too much to post 2 classes and 1 interface here. I posted the a small part to give some context, i will update the question and add the full sample if it helps. – Aditya Apr 17 '13 at 09:39
  • I have updated the question and added the code now. Hope it helps. – Aditya Apr 17 '13 at 09:48
  • @gap_j: Well you've updated the code, which is good - but it's still not clear what you *are* getting vs what you *expect* to get. If you're really just interested in the ClassCastException, then there's no point in having any methods with arguments. (And there's definitely no point in including the non-Javadoc comments.) Basically you need to trim the code to be short, complete, and still representative of just the situation you want us to understand. – Jon Skeet Apr 17 '13 at 09:50
  • I'm interested only in the ClassCastException, but i thought it would be helpful enumerating the cases to save others the time. But i guess what you are saying is right, i've removed the code that might not be the cause. And thanks for the tips! – Aditya Apr 17 '13 at 10:20
0

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.

mki
  • 625
  • 3
  • 9
  • 1
    my original code post had 2 similar methods, and got the exception you are getting. But in my case the method throwimg exceptions has no arguments, i modified the example to reflect that.. *I am interested only in the reasons the ClassCastException* is thrown as i am able to recreate the other cases. (Refer to jon skeet's answer and the comments) – Aditya Apr 22 '13 at 04:38