へのアクセスを得たいです。のパッケージプライベートコンストラクタです。すなわち ラムダ式は、スコープ外のクラスのプライベートメソッドにアクセスできますか?
、この1:/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
は、それが十分に簡単であるためMethodHandleを作成し、それを呼び出しています。 Reflectionを直接使用する場合も同じです。
しかし、機能的なインターフェイスを介してコンストラクタを直接呼び出すことができるかどうかは不思議です。
27602758多少似たような問題がありますが、提供されたソリューションはこのような場合には動作しません。
以下のテストケースは問題なくコンパイルされます。実際のインターフェイス呼び出しを除いて、すべてが機能します。このエラーは、まだアクセス表向き付与されているにもかかわらず、スローされるのはなぜ
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)
:
shared.create("foo".toCharArray(), true)
package test;
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public class Test {
// Creates a new String that shares the supplied char[]
private static interface StringCreator {
public String create(char[] value, boolean shared);
}
// Creates a new conventional String
private static String create(char[] value, boolean shared) {
return String.valueOf(value);
}
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the calling class
Lookup caller = MethodHandles.lookup();
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for #create()
MethodHandle conventional = caller.findStatic(
Test.class, "create", MethodType.methodType(String.class, char[].class, boolean.class)
);
StringCreator normal = getStringCreator(caller, conventional);
System.out.println(
normal.create("foo".toCharArray(), true)
// prints "foo"
);
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// test directly if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = (String) constructor.invokeExact(chars, true);
chars[0] = 'b'; // modify array contents
chars[1] = 'a';
chars[2] = 'r';
System.out.println(
s
// prints "bar"
);
// generate interface for constructor
StringCreator shared = getStringCreator(caller, constructor);
System.out.println(
shared.create("foo".toCharArray(), true)
// throws error
);
}
// returns a StringCreator instance
private static StringCreator getStringCreator(Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"create",
MethodType.methodType(StringCreator.class),
handle.type(),
handle,
handle.type()
);
return (StringCreator) callSite.getTarget().invokeExact();
}
}
Specficially命令は、次のエラーがスローされますか?
誰もが生成されたインターフェイスは、そのすべてのコンポーネントがアクセスする方法にアクセスできない理由の説明を考え出すことができますか?
純粋なReflectionまたはMethodHandlesに戻らずに、この特定の使用例で実際に機能する解決法または実行可能な代替品がありますか?
私は困惑しています。
決定的にあなたの実際の質問に答えることはできませんしかし、純粋な反省はここではひどいものではありません。メソッド/コンストラクタ/ etc基本的に、生成されたバイトコードを持つアクセサの周りのプロキシクラスです。それらを作成するのは大量の操作ですが、呼び出しはできません。 'boolean'はボックス化されますが、少なくともSun/OpenJDKの実装では、' new'を使うこととほぼ同じです。アクセスは、メンバーが属しているスコープの内部にあるかのようにアクセサを生成することによってアクセスエラーを回避します。LambdaMetaFactoryはここでやっていないと思います。 – Radiodef
@Radiodef詳細をありがとうございます。ラムダ解が見つからない場合は、単にMethodHandleを使用します。私は、なぜテストがそれと同じように動作するのか、ほとんど不思議です。 –