Ответ 1
Для меня это похоже на ошибку в JVM. Системный загрузчик системы пытается найти преобразованный класс по его имени. Однако лямбда-выражения загружаются через загрузку анонимного класса, где выполняется следующее условие:
clazz.getClassLoader()
.loadClass(clazz.getName().substring(0, clazz.getName().indexOf('/')))
дает a ClassNotFoundException
, что приводит к NoClassDefError
. Класс не считается реальным классом, и такие анонимные классы, например, не передаются в ClassFileTransformer
вне ретрансляции.
В целом, API-интерфейс инструментария чувствует себя немного ошибкой при работе с анонимными классами. Точно так же LambdaForm
передаются ClassFileTransformer
, но со всеми аргументами, но classFileBuffer
установлен на null
, что нарушает контракт класса трансформатора.
Для вашего примера проблема заключается в том, что вы возвращаете null
; проблема исчезает, когда возвращается classFileBuffer
то, что нет-op. Это не то, что предлагает ClassFileTransformer
, где возвращение null
является рекомендуемым способом:
правильно сформированный буфер файла класса (результат преобразования) или
null
, если преобразование не выполняется.
Для меня это похоже на ошибку в HotSpot. Вы должны сообщить об этой проблеме в OpenJDK.
В целом, вполне возможно использовать анонимно загруженные классы, как я демонстрирую в своей библиотеке манипулирования кодом Byte Buddy. Это требует некоторых неудачных настроек по сравнению с обычными инструментами, но среда выполнения поддерживает его. Вот пример, который успешно работает как unit test в библиотеке:
Callable<String> lambda = () -> "foo";
Instrumentation instrumentation = ByteBuddyAgent.install();
ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation)
.preregistered(lambda.getClass());
ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation,
lambda.getClass());
assertThat(lambda.call(), is("foo"));
new ByteBuddy()
.redefine(lambda.getClass(), classFileLocator)
.method(named("call"))
.intercept(FixedValue.value("bar"))
.make()
.load(lambda.getClass().getClassLoader(), classReloadingStrategy);
assertThat(lambda.call(), is("bar"));