Java Lambda表达式能否访问其作用域之外的类的私有方法?
我想获得对java.lang.String的包私有构造函数的反射访问权 即这个, 为它创建MethodHandle非常简单,调用它也非常简单。 直接使用反射也是如此 但是我很好奇是否可以通过函数接口直接调用构造函数 涉及到一个类似的问题,但提供的解决方案在这种情况下似乎不起作用 下面的测试用例编译时没有问题。除了实际的接口调用之外,一切都正常。 特别是指令 引发以下错误:Java Lambda表达式能否访问其作用域之外的类的私有方法?,java,reflection,lambda,Java,Reflection,Lambda,我想获得对java.lang.String的包私有构造函数的反射访问权 即这个, 为它创建MethodHandle非常简单,调用它也非常简单。 直接使用反射也是如此 但是我很好奇是否可以通过函数接口直接调用构造函数 涉及到一个类似的问题,但提供的解决方案在这种情况下似乎不起作用 下面的测试用例编译时没有问题。除了实际的接口调用之外,一切都正常。 特别是指令 引发以下错误: Exception in thread "main" java.lang.IllegalAccessError: tr
Exception in thread "main" java.lang.IllegalAccessError: tried to access method java.lang.String.<init>([CZ)V from class test.Test$$Lambda$2/989110044 at test.Test.main(Test.java:59)
线程“main”java.lang.IllegalAccessError中出现异常:试图访问方法java.lang.String。([CZ)V来自类test.test$$Lambda$2/989110044 at test.test.main(test.java:59)
尽管表面上允许访问,但为什么仍会抛出此错误?
有人能解释为什么生成的接口无法访问其所有组件都可以访问的方法吗
是否有一种解决方案或可行的替代方案可以在不恢复到纯反射或MethodHandles的情况下实际用于此特定用例
因为我被难住了。问题是您重写了要信任的查找对象,因此它对
字符串的私有方法的访问将通过查找过程和lambda元工厂,但它仍然绑定到您的Test
类,因为该类是通过方法handl创建查找对象的类es.lookup()
和生成的类将生活在相同的上下文中。JVM在这些生成的类的可访问性方面非常慷慨,但显然,从生活在应用程序类上下文中的类访问引导类java.lang.String
的private
成员是不被接受的
您可以通过(String.class)
中的MethodHandles.lookup().in(然后将其修补为具有private
或“可信”访问权限)获得位于适当上下文中的查找对象,但是,您将遇到另一个问题:一个位于java.lang.String
上下文中的类(或仅在引导加载程序的上下文中)将无法访问自定义的接口StringCreator
,并且无法实现它
唯一的解决方案是使用位于String
上下文中的查找对象,并实现一个现有的通用接口
s,可从引导类加载器访问:
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.function.BiFunction;
public class Test {
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the String class
Lookup caller = MethodHandles.lookup().in(String.class);
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// generate interface implementation for constructor
BiFunction<char[],Boolean,String> shared=getStringCreator(caller, constructor);
// test if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = shared.apply(chars, true);
chars[0] = 'b'; chars[1] = 'a'; chars[2] = 'r';// modify array contents
System.out.println(s); // prints "bar"
chars[0] = '1'; chars[1] = '2'; chars[2] = '3';
System.out.println(s); // prints "123"
}
private static BiFunction<char[],Boolean,String> getStringCreator(
Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"apply",
MethodType.methodType(BiFunction.class),
handle.type().generic(),
handle,
handle.type()
);
return (BiFunction) callSite.getTarget().invokeExact();
}
}
import java.lang.invoke.*;
导入java.lang.invoke.MethodHandles.Lookup;
导入java.lang.reflect.Field;
导入java.util.function.BiFunction;
公开课考试{
公共静态void main(字符串[]args)抛出可丢弃的{
//反射地为String类生成受信任的查找
Lookup caller=MethodHandles.Lookup().in(String.class);
字段模式=Lookup.class.getDeclaredField(“allowedModes”);
modes.setAccessible(true);
modes.setInt(调用者,-1);//-1==Lookup.TRUSTED
//为共享字符串构造函数创建句柄
MethodHandle构造函数=caller.findConstructor(
String.class、MethodType.MethodType(void.class、char[].class、boolean.class)
);
//为构造函数生成接口实现
BiFunction shared=getStringCreator(调用者、构造函数);
//测试construcor是否被正确访问
char[]chars=“foo”.toCharArray();
字符串s=shared.apply(字符,true);
字符[0]=“b”;字符[1]=“a”;字符[2]=“r”;//修改数组内容
System.out.println(s);//打印“bar”
字符[0]=“1”;字符[1]=“2”;字符[2]=“3”;
System.out.println(s);//打印“123”
}
私有静态双功能getStringCreator(
查找调用方,MethodHandle(句柄)抛出Throwable{
CallSite CallSite=LambdaMetafactory.metafactory(
呼叫者
“申请”,
MethodType.MethodType(双函数类),
handle.type().generic(),
手柄
handle.type()
);
return(双函数)callSite.getTarget().invokeExact();
}
}
无法明确回答您的实际问题,但这里的纯反射并不可怕。方法/构造函数/等本质上是访问器周围的代理类,具有生成的字节码。创建它们是一项繁重的操作,但调用不是。布尔值被装箱,但在其他方面与使用新的
,至少在Sun/OpenJDK实现中是这样。反射通过生成访问器来绕过访问错误,就像它在成员所属的范围内一样,我猜LambdaMetaFactory在这里没有这样做。@Radiodef感谢您提供的详细信息。如果找不到lambda解决方案,我将使用MethodHandle。我主要是c非常感谢您的详细解释和有效的解决方案!我不认为有任何方法可以让这样的查找在引导类装入器之外了解类?
shared.create("foo".toCharArray(), true)
Exception in thread "main" java.lang.IllegalAccessError: tried to access method java.lang.String.<init>([CZ)V from class test.Test$$Lambda$2/989110044 at test.Test.main(Test.java:59)
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.function.BiFunction;
public class Test {
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the String class
Lookup caller = MethodHandles.lookup().in(String.class);
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// generate interface implementation for constructor
BiFunction<char[],Boolean,String> shared=getStringCreator(caller, constructor);
// test if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = shared.apply(chars, true);
chars[0] = 'b'; chars[1] = 'a'; chars[2] = 'r';// modify array contents
System.out.println(s); // prints "bar"
chars[0] = '1'; chars[1] = '2'; chars[2] = '3';
System.out.println(s); // prints "123"
}
private static BiFunction<char[],Boolean,String> getStringCreator(
Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"apply",
MethodType.methodType(BiFunction.class),
handle.type().generic(),
handle,
handle.type()
);
return (BiFunction) callSite.getTarget().invokeExact();
}
}