
@Test public void likeReflectTest() throws Throwable { Class<MethodHadles.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 2024-04-04 10:33:26 +08:00 问大语言模型比在这里提问会来的更快 大语言模型说 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 2024-04-04 11:22:56 +08:00 @Parkerz 你要执行的 Spring 对象的方法是 Spring 管理的对象的方法么? |
4 darkengine 2024-04-04 11:40:45 +08:00 @hnliuzesen 看了代码他要反射的不是 String 类 。。。 |
5 Parkerz OP @hnliuzesen 这里的对象和 spring 无关。 |
6 Parkerz OP @darkengine 我看有个 up 用 String 演示的,我用自定义的对象来试发现不行 |
7 rayae 2024-04-04 22:12:24 +08:00 高版本 jdk 反射要加 VM 参数 |
8 hnliuzesen 2024-04-05 13:24:34 +08:00 |
9 hnliuzesen 2024-04-06 15:09:51 +08:00 试到最后,没有报 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 2024-04-07 20:00:13 +08:00 via Android @Parkerz 这个比反射效率高,感觉主要是让方法引用用的。我建议先用正常的 lambda 表达式来当 ToIntFunction2 的实现来实现你想要的效果,然后用 javap 查看编译出来的 class 文件,里面应该是正确的实现方法 |
12 Aresxue 2024-04-09 15:33:38 +08:00 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 2024-04-13 10:00:50 +08:00 这段代码应当改为 ```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 2024-04-18 11:09:18 +08:00 via iPhone 两年前写过这个黑魔法,注意自定义函数接口要声明序列化 |