最近尝试通过库来访问clone
数组类型的方法。java.lang.invoke
这被证明是不成功的。示例中的克隆方法是这样的:
int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();
我希望MethodHandle
为a.clone()
通话创建一个。这样生成的代码类似于:
int[] a = new int[]{1, 2, 3, 4};
int[] b = findCloneMethod().invoke(a);
我为所有其他方法调用设置了这个系统。但是,仅使用这种方法,系统就会失败。
这个问题与java.lang.invoke
图书馆有关,而不是java.lang.reflect
图书馆。
问题描述
引入了以下示例代码来显示此行为。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Main {
void test() {
final MethodHandles.Lookup caller = MethodHandles.lookup();
final Class<?> refc = int[].class;
final String name = "clone";
final MethodType type = MethodType.methodType(Object.class);
final MethodHandle mh = findVirtual(caller, refc, name, type);
System.out.println("Lookup: " + caller);
System.out.println("mh: " + mh);
}
public static void main(String[] args) {
new Main().test();
}
private MethodHandle findVirtual(final MethodHandles.Lookup caller, final Class<?> refc, final String name, final MethodType type) {
try {
return caller.findVirtual(refc, name, type);
} catch (final Throwable t) {
t.printStackTrace();
return null;
}
}
}
样本输出:
查找:主要
mh:方法句柄(主)对象
输出是有道理的。范围内的调用者Main
只能看到的超类clone
中的方法Main
:Object
。即使int[]
用作参考类,此方法也是不可见的。这成为一个问题,因为如果使用此调用站点,JVM 会尝试强制转换int[]
为Main
.
通过将引用类型引入为int[]
,人们会期望clone
type 中的方法int[]
是可公开访问的。因此,当文档建议时:
方法句柄的类型将是方法的类型,并带有接收者类型(通常是 refc)。1
的类型mh
将是(int[])Object
。
尝试的解决方案:
由于查找caller
通过遮蔽正确的clone
方法进行干扰,因此尝试使用公共查找:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
然而,这失败了。Object#clone()
对签名的简单检查说明了原因:
protected Object clone() throws CloneNotSupportedException { ...
方法是protected
。这不适用于数组类型,因为实现使该方法公开可用。
这方面的一个例子是尝试访问该Object#equals
方法。这与三个单独的参考类一起显示:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
final Class<?> refc = Object.class;
final String name = "equals";
final MethodType type = MethodType.methodType(boolean.class, Object.class);
查找:java.lang.Object/public
mh:方法句柄(对象,对象)布尔
然后Main.class
用作参考类:
final Class<?> refc = Main.class;
查找:java.lang.Object/public
mh:方法句柄(主要,对象)布尔
然后int[].class
用作参考类:
final Class<?> refc = int[].class
查找:主要
mh: MethodHandle(int[],Object)boolean
这是预期的行为,显然可以流畅地工作。在第一种情况下,引用类是Object
并且它返回Object#equals
方法句柄。第二个是引用类Main
,它返回Main#equals
方法句柄。最后,引用类是int[].class
并且它返回了int[]#equals
方法句柄。
但是,该方法不能保持这种奇偶性int[]#clone
。由于该clone
方法是受保护的,但对于数组是公共的,因此无法找到它。对我来说,这似乎是一个错误。数组clone
方法应该通过publicLookup
. 返回的方法句柄类型可能是:MethodHandle(Object)Object
。
一旦检索到,MethodHandle#asType
然后可以用于更正类型。在这种情况下,它将被转换为MethodHandle(int[])Object
.
如果没有Object#clone
公开可用的方法,至少对于数组类型,这似乎是不可能的。
此外,这似乎是唯一可能发生这种情况的方法,因为数组类型仅扩展/实现 3 个类Object
:Cloneable
和Serializable
. 唯一的其他方法Object#finalize
也是受保护的;但是,数组类型不会公开此方法。因此clone
,这是唯一失败的方法。
tl;博士:
数组clone
方法,例如Object[]#clone()
,不能通过公共查找公开访问。这是因为Object#clone
是protected
;但是,数组类型使此方法公开可用。clone
由于此可见性问题,尝试通过引用类访问数组失败。因此,MethodHandle
无法为此方法生成 a。但是,在公共方法上尝试相同的技术,例如Object#equals
对数组类型效果很好。
我宁愿避免反射访问,因为方法可以通过简单地更改查找的信任级别来检索它。
有没有办法MethodHandle
为数组clone
方法生成一个?
注意:我确实了解 的正确用法,java.lang.invoke
并且我不打算将其用作java.lang.reflect
.