@Test
public void likeReflectTest() throws Throwable {
Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;
Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP");
implLookup.setAccessible(true);
MethodHandles.Lookup lookup = (MethodHandles.Lookup)implLookup.get(null);
MethodHandle getSum = lookup
.in(Phone.class)
.findSpecial(
Phone.class,
"getSum",
methodType(int.class, int.class, int.class),
Phone.class
);
CallSite applyAsInt = LambdaMetafactory.metafactory(
lookup,
"applyAsInt",
methodType(ToIntFunction2.class),
methodType(int.class, Phone.class, int.class, int.class),
getSum,
methodType(int.class, Phone.class, int.class, int.class)
);
ToIntFunction2 string
= (ToIntFunction2) applyAsInt.getTarget().invoke();
int intResult = string.applyAsInt(new Phone(), 1, 0);
System.out.println(intResult);
}
ToIntFunction2.java 是这样的:
@FunctionalInterface
public interface ToIntFunction2 {
int applyAsInt( Phone value1, int value2, int value3);
}
Phone.java 的一个方法是这样的:
int getSum(int param1, int param2) {
return param1 + param2;
}
最后执行出现了 ClassNotDefFound ,哥哥们,是啥原因呢
1
hnliuzesen 243 天前
问大语言模型比在这里提问会来的更快
大语言模型说 1. findSpecial 用来调用 private 方法,但是 getSum 不是 private 方法 2. findSpecial 中 getSum 的参数数量与实际不符 3. LambdaMetafactory 中也是参数不符 ``` @Test public void likeReflectTest() throws Throwable { Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class; Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP"); implLookup.setAccessible(true); MethodHandles.Lookup lookup = (MethodHandles.Lookup)implLookup.get(null); // Assuming getSum is private, otherwise findVirtual should be used. MethodHandle getSum = lookup .in(Phone.class) .findSpecial( Phone.class, "getSum", methodType(int.class, int.class, int.class), Phone.class ); CallSite applyAsInt = LambdaMetafactory.metafactory( lookup, "applyAsInt", methodType(ToIntFunction2.class), methodType(int.class, Phone.class, int.class, int.class), // This should match the interface's method signature. getSum, getSum.type() // This should be the method handle's type. ); ToIntFunction2 func = (ToIntFunction2) applyAsInt.getTarget().invoke(); int intResult = func.applyAsInt(new Phone(), 1, 0); System.out.println(intResult); } ``` |
2
Parkerz OP @hnliuzesen 我问了好几个大模型( gpt ,kimi...,也改过楼主说的这个)但是根据他们的建议无一例外全部错了,所以我想问问有没有 v 站的大佬懂这种用法(›´ω`‹ )
|
3
hnliuzesen 243 天前
@Parkerz 你要执行的 Spring 对象的方法是 Spring 管理的对象的方法么?
|
4
darkengine 243 天前
@hnliuzesen 看了代码他要反射的不是 String 类 。。。
|
5
Parkerz OP @hnliuzesen 这里的对象和 spring 无关。
|
6
Parkerz OP @darkengine 我看有个 up 用 String 演示的,我用自定义的对象来试发现不行
|
7
rayae 243 天前
高版本 jdk 反射要加 VM 参数
|
8
hnliuzesen 242 天前
|
9
hnliuzesen 241 天前
试到最后,没有报 ClassNotDefFound ,但是提示 Invalid receiver type interface ToIntFunction2; not a subtype of implementation type class Phone 。
如果不改 invokedType ,过不去 validateMetafactoryArgs 的验证 getSum 接收的参数数量也和 applyAsInt 也不一样 最终是没成功运行 不知道这段代码的目的是不是想把 getSum 当作是 ToIntFunction2 里 applyAsInt 的实现,如果是的话,感觉可能是生成 CallSite 的方式不合适 |
10
Parkerz OP @hnliuzesen 我也不懂这种用法,听说 FastJSON 源码里都是这种
|
11
hnliuzesen 240 天前 via Android
@Parkerz 这个比反射效率高,感觉主要是让方法引用用的。我建议先用正常的 lambda 表达式来当 ToIntFunction2 的实现来实现你想要的效果,然后用 javap 查看编译出来的 class 文件,里面应该是正确的实现方法
|
12
Aresxue 238 天前
https://pebble-skateboard-d46.notion.site/Java-7d1e6f877c9d4d02811e1181bc5b361c?pvs=25 看我这篇文章吧,会对方法句柄和 Lambda 有一个更深的了解。
|
14
Parkerz OP @Aresxue 加上这一步就解决了,感谢~ ,但是比较奇怪,为啥 JDK 的 String 中的 coder()方法不需要加这一句就能成功执行到。
'''MethodHandles.Lookup lookup = TRUSTED.in(xxxx.class);''' |
15
nieyuanhong 234 天前 1
这段代码应当改为
```java CallSite applyAsInt = LambdaMetafactory.metafactory( lookup.in(Phone.class), "applyAsInt", methodType(ToIntFunction2.class), methodType(int.class, Phone.class, int.class, int.class), getSum, methodType(int.class, Phone.class, int.class, int.class) ); ``` 因为调用 metafactory 方法时, 方法内部初步生成的 Lambda 字节码会被作为 lookup.in(Phone.class)的内部类加载, 加载时, 会调用方法 java.lang.invoke.InnerClassLambdaMetafactory#generateInnerClass, 具体逻辑形如 ```java //jdk21 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper) .defineClass(!disableEagerInitialization, classdata); ``` 在 defineClass 中逻辑形如 ```java //jdk21 Class<?> lookupClass = lookup.lookupClass(); ClassLoader loader = lookupClass.getClassLoader(); //... ``` 这里的第一句 lookup.lookupClass() 获取的就是 metafactory 的第一个参数 lookup.in(Phone.class)中的 Phone.class, 如果用原始的 lookup 的话, 会获取到 Object.class, 显然 Object 的 classloader 会找不到 Phone.class, 但是可以找到其他和它位于同个 classloader 中的类, 比如 String.class. |
16
siweipancc 229 天前 via iPhone
两年前写过这个黑魔法,注意自定义函数接口要声明序列化
|