Я пытаюсь создать небольшую утилиту, чтобы заменить использование отражений во всем проекте (в основном для повышения производительности при использовании LambdaMetafactory), но я спотыкаюсь при создании CallSite. Однако эта проблема возникает только при доступе к классам, которые мне не принадлежат. Доступ к сторонним библиотекам или даже к собственным классам Java (например, java .lang.Object) приведет к ошибке NoClassDefFoundError не для стороннего класса, а для моего интерфейса.
public final class Accessor {
private static Constructor<MethodHandles.Lookup> lookupConstructor;
static {
newLookupConstructor();
}
protected static void newLookupConstructor() {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("MethodHandles.Lookup class constructor (Class) not found! Check java version.");
}
}
private Accessor() { }
public static <T> T to(Class<T> interfaze, Class<?> clazz, String method, Class<?>... params) {
try {
return to(interfaze, clazz.getDeclaredMethod(method, params));
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
public static <T> T to(Class<T> interfaze, Method method) {
try {
MethodHandles.Lookup caller = lookupConstructor.newInstance(method.getDeclaringClass());
MethodHandle implMethod = caller.unreflect(method);
CallSite site = LambdaMetafactory.metafactory(caller, method.getName(), MethodType.methodType(interfaze), implMethod.type(), implMethod, implMethod.type());
// ^ java.lang.NoClassDefFoundError for the passed interfaze class
return (T) site.getTarget().invoke();
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
Юнит-тесты I ' Вы можете запустить демонстрацию проблемы здесь:
final class AccessorTest {
@Test // SUCCESS
@DisplayName("Verify MethodHandles.Lookup constructor")
void lookupConstructorAvailabilityTest() {
Assertions.assertDoesNotThrow(() -> Accessor.newLookupConstructor());
}
@Test // SUCCESS
@DisplayName("Verify available matching instance method is called")
void findMatchingMethodAndCallTest() {
ObjectAccessor accessor = Accessor.to(ObjectAccessor.class, TestObject.class, "instanceMethod");
Assertions.assertNotNull(accessor);
Assertions.assertTrue(accessor.instanceMethod(new TestObject()));
}
@Test // SUCCESS
@DisplayName("Verify available matching static method is called")
void findMatchingStaticMethodAndCallTest() {
ObjectAccessor accessor = Accessor.to(ObjectAccessor.class, TestObject.class, "staticMethod");
Assertions.assertNotNull(accessor);
Assertions.assertTrue(accessor.staticMethod());
}
@Test // FAILURE
@DisplayName("Verify java.lang.Object#toString works")
void testDynamicToStringInvokation() {
ToString accessor = Accessor.to(ToString.class, Object.class, "toString");
// ^ java.lang.NoClassDefFoundError: com/gmail/justisroot/autoecon/data/AccessorTest$ToString
Assertions.assertNotNull(accessor);
Assertions.assertEquals(accessor.toString(Integer.valueOf(42)), "42");
}
public interface ObjectAccessor {
public boolean instanceMethod(TestObject o);
public boolean staticMethod();
}
public interface ToString {
public String toString(Object o);
}
}
Это выдаст следующее:
java .lang.NoClassDefFoundError: com / gmail / justisroot / autoecon / data / AccessorTest $ ToString в java .base / jdk.internal.mis c .Unsafe.defineAnonymousClass0 (собственный метод) в java .base / jdk.internal.mis c .Unsafe.defineAnonymousClass (небезопасно. java: 1223) в java .base / java .lang.invoke.InnerClassLambdaMetafactory.spinInnerClass (InnerClassLambdaMetafactory. java: 320) в java .base / java .lang.invoke.Innerelassamb. buildCallSite (InnerClassLambdaMetafactory. java: 188) в java .base / java .lang.invoke.LambdaMetafactory.metafactory (LambdaMetafactory. java: 317) в com.gmail.justis root .autocon .Accessor.to (Accessor. java: 43)
Я слишком много времени тратил на то, чтобы ломать голову над решениями. Вторая пара глаз наверняка будет полезна.
Что я делаю не так?