如何通过公共查找获取数组clone方法的MethodHandle?
如何用MethodHandle正确调用数组的clone方法?
我明白你遇到的问题了——数组的clone方法确实是个特殊情况,用常规的findVirtual调用根本拿不到正确的方法句柄,这是因为JVM对数组类型的clone做了特殊实现,和普通类的方法查找逻辑不一样。
问题根源
数组的clone方法并不是继承自Object的那个protected方法,而是JVM为每个数组类型单独实现的公共方法,但它并没有在数组类的字节码中显式声明。这就导致:
- 当你用当前类(比如
Main)的Lookup实例调用findVirtual时,它只会找到Object的protectedclone方法,而且这个方法的作用域被限制在Main类的上下文里,调用时会错误地尝试把数组转成Main类型。 - 用
publicLookup的话,又因为Object的clone是protected的,直接查找会失败,而数组的特殊clone方法又不在常规的查找路径里。
解决办法
针对不同的Java版本,有两种可靠的解决方案:
方案1:Java 9+ 直接用JDK提供的工具方法
从Java 9开始,MethodHandles专门提供了arrayClone()方法,一步到位获取数组克隆的方法句柄,这是最简洁的方式:
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.Arrays; public class Main { void test() { MethodHandle arrayCloneHandle = MethodHandles.arrayClone(int[].class); try { int[] a = new int[]{1, 2, 3, 4}; int[] b = (int[]) arrayCloneHandle.invoke(a); System.out.println("克隆后的数组:" + Arrays.toString(b)); } catch (Throwable t) { t.printStackTrace(); } } public static void main(String[] args) { new Main().test(); } }
这个方法会直接返回对应数组类型的方法句柄,调用时传入原数组就能得到同类型的克隆数组,完全不需要操心方法查找的问题。
方案2:Java 8及以下的兼容方案
如果还在维护Java 8的项目,可以先通过反射拿到数组的clone方法,再转换成MethodHandle——反射对数组的特殊方法有专门的处理逻辑,能正确找到公共的clone方法:
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.Arrays; public class Main { void test() { try { // 用反射获取数组的clone方法,反射能识别JVM的特殊实现 Method cloneMethod = int[].class.getMethod("clone"); // 将反射的Method转换为MethodHandle MethodHandle arrayCloneHandle = MethodHandles.lookup().unreflect(cloneMethod); int[] a = new int[]{1, 2, 3, 4}; int[] b = (int[]) arrayCloneHandle.invoke(a); System.out.println("克隆后的数组:" + Arrays.toString(b)); } catch (Throwable t) { t.printStackTrace(); } } public static void main(String[] args) { new Main().test(); } }
这里int[].class.getMethod("clone")能正确拿到数组的公共clone方法,再通过unreflect转换成方法句柄就可以正常调用了。
为什么你的原代码不行?
再回头看你的测试代码:
- 用
Main类的Lookup调用findVirtual时,查找逻辑会优先找当前作用域可见的方法,也就是Object的protectedclone,所以拿到的方法句柄绑定的是Object的方法,调用时自然会出现类型转换错误。 - 用
publicLookup时,因为Object的clone是protected的,常规的findVirtual找不到数组的特殊clone方法,所以直接失败。
数组的方法处理确实有不少特殊之处,这也是为什么JDK后来专门加了arrayClone工具方法来简化操作。
内容的提问来源于stack exchange,提问作者Obicere
相关产品推荐
相关产品推荐

