3

I have an issue with invokeExact method when passing it an instance created with newInstance method.

My method looks something like this

public <T extends BaseEntity> T getEntity(Class<T> entityClass) {

    T entity;

    try {
        entity = entityClass.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }

    for (Field field : map.get(entityClass).keySet()) {
        if (get(field.getName()) != null) {
            try {
                MethodHandle methodHandle = map.get(entityClass).get(field);
                methodHandle.invokeExact(entity, (String) get(field.getName()));
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    }

    return entity;
}

The method is invoked like this

resource.getEntity(DemoEntity.class);

end that results in

Caused by: java.lang.invoke.WrongMethodTypeException: **expected (DemoEntity,String)void but found (BaseEntity,String)void**
at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:349) ~[na:1.7.0_45]
at java.lang.invoke.Invokers.checkExactType(Invokers.java:360) ~[na:1.7.0_45]
at com.me.api.entity.request.DemoEntityRequest.getEntity(DemoEntityRequest.java:49) ~[classes/:na]
... 27 common frames omitted

If I use invoke instead of invokeExact the code works fine, but I'm curious to know what is causing the problem i.e. why invokeExact method sees T entity as its super class type and not as a DemoEntity?

EDIT:

Here is a full working example

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;

public class InvokeExactTest {

    public static void main(String[] args) {
        new InvokeExactTest().test(Demo.class);
    }

    public <T extends Base> void test(Class<T> clazz) {
        Field field = clazz.getDeclaredFields()[0];
        field.setAccessible(true);
        MethodHandle setter = null;
        try {
            setter = MethodHandles.lookup().unreflectSetter(field);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        try {
            Demo demo1 = new Demo();
            setter.invokeExact(demo1, "Demo 1");
        } catch (Throwable e) {
            e.printStackTrace();
        }

        try {
            T demo2 = clazz.newInstance();
            setter.invokeExact(demo2, "Demo 2");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static class Base {
    }

    public static class Demo extends Base {
        private String name;
    }
}

Demo 1 will work, demo 2 will fail with

    java.lang.invoke.WrongMethodTypeException: expected (Demo,String)void but found (Base,String)void
at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:294)
at java.lang.invoke.Invokers.checkExactType(Invokers.java:305)
at InvokeExactTest.test(InvokeExactTest.java:30)
at InvokeExactTest.main(InvokeExactTest.java:8)
eremic
  • 113
  • 9
  • Can you add a complete (but minimal) example that reproduces this? – Sotirios Delimanolis Mar 23 '15 at 21:51
  • Presumably the `MethodHandle` was acquired with a reference class of `BaseEntity`. That would set its method type to something like `(BaseEntity,String)void`. You won't be able to `invokeExact` with a `DemoEntity`. – Sotirios Delimanolis Mar 23 '15 at 22:02
  • @SotiriosDelimanolis I've added a full working example that demonstrated my issue. – eremic Mar 23 '15 at 22:13
  • If I change demo 2 to Demo demo2 = (Demo) clazz.newInstance(), code will work, so it seems that invokeExact requires exact type but compile time? – eremic Mar 23 '15 at 22:31

1 Answers1

1

To answer my question, invokeExact requires the type to be know at compile time and there is no way around it.

eremic
  • 113
  • 9