2016-03-23 15 views
6

へのアクセスを得たいです。のパッケージプライベートコンストラクタです。すなわち ラムダ式は、スコープ外のクラスのプライベートメソッドにアクセスできますか?

、この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に戻らずに、この特定の使用例で実際に機能する解決法または実行可能な代替品がありますか?

私は困惑しています。

+1

決定的にあなたの実際の質問に答えることはできませんしかし、純粋な反省はここではひどいものではありません。メソッド/コンストラクタ/ etc基本的に、生成されたバイトコードを持つアクセサの周りのプロキシクラスです。それらを作成するのは大量の操作ですが、呼び出しはできません。 'boolean'はボックス化されますが、少なくともSun/OpenJDKの実装では、' new'を使うこととほぼ同じです。アクセスは、メンバーが属しているスコープの内部にあるかのようにアクセサを生成することによってアクセスエラーを回避します。LambdaMetaFactoryはここでやっていないと思います。 – Radiodef

+0

@Radiodef詳細をありがとうございます。ラムダ解が見つからない場合は、単にMethodHandleを使用します。私は、なぜテストがそれと同じように動作するのか、ほとんど不思議です。 –

答えて

2

問題はStringprivate方法へのアクセスは、検索手順とラムダメタ工場を通過しますので、あなたは、信頼されるために、ルックアップオブジェクトをオーバーライドすることですが、それが作成したクラスだとして、そのまだあなたTestクラスにバインドルックアップオブジェクトはMethodHandles.lookup()を介して生成されたクラスは同じコンテキスト内に存在します。 JVMはこれらの生成されたクラスに関してアクセシビリティに関してかなり寛大ですが、明らかにアプリケーションクラスのコンテキストにあるクラスからブートストラップクラスjava.lang.Stringprivateメンバーにアクセスすることは受け入れられません。

適切なコンテキストに存在する検索オブジェクトを、 MethodHandles.lookup() .in(String.class)(その後、または「信頼できる」アクセスがあるようにパッチを当てる)、別の問題が発生する:java.lang.Stringのコンテキストにあるクラス(またはブートストラップローダーのコンテキスト内にある)が、カスタムinterface StringCreatorにアクセスできないそれを実装することはできません。

唯一の解決策は、ブートストラップクラスローダからアクセスでき、検索対象Stringの文脈での生活や既存の汎用interface秒のいずれかを実装を使用することです:

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(); 
    } 
} 
+0

詳細な説明、および実用的なソリューションをお寄せいただきありがとうございます!そのようなルックアップにブートストラップクラスローダー以外のクラスについて学ぶ方法があるとは思いませんか? –

関連する問題