最近,我尝试通过clone
库访问数组类型的java.lang.invoke
方法。事实证明这是不成功的。样本中的克隆方法就是这样的:
int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();
我希望为a.clone()
电话创建MethodHandle
。这样生成的代码类似于:
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:MethodHandle(Main)Object
输出很有意义。来自Main
范围的调用者仅在clone
的超类Main
中看到Object
方法。即使int[]
用作引用类,此方法也不可见。这成为一个问题,因为如果使用此调用站点,JVM会尝试将int[]
强制转换为Main
。
通过将引用类型引入为int[]
,可以预期类型clone
中的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:MethodHandle(Object,Object)boolean
然后使用Main.class
作为参考类:
final Class<?> refc = Main.class;
查找:java.lang.Object / public
mh:MethodHandle(Main,Object)boolean
然后使用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
是唯一失败的方法。
数组clone
方法(例如Object[]#clone()
)无法通过公共查找公开访问。这是因为Object#clone
是protected
;但是,数组类型使此方法公开可用。由于此可见性问题,尝试通过引用类访问数组clone
失败。因此,无法为此方法生成MethodHandle
。但是,在Object#equals
等公共方法上尝试相同的技术可以很好地适用于数组类型。
我更愿意避免使用反射访问,因为只需更改查找的信任级别即可检索它。
有没有办法为数组MethodHandle
方法生成clone
?
注意:我确实理解java.lang.invoke
的正确用法,我不打算用它来代替java.lang.reflect
。
答案 0 :(得分:2)
在JSR 292的早期实现中存在一个错误JDK-8001105,其中<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form>
<input type="text" id="name" placeholder="Name">
<input type="button" class="add-row" value="Add Row">
</form>
<table border="1">
<thead>
<tr>
<th>Name</th><th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>Peter Parker</td><td style="cursor:pointer;" onclick="del(this);">Delete</td>
</tr>
</tbody>
</table>
不适用于数组的findVirtual
方法。但是,这个问题很久以前就已经解决了。
因此,在最近的JDK 8 clone
中findVirtual
如果在clone
上调用则可以正常工作。{
您可以在JDK source code中找到一个特殊情况:
publicLookup()