2016-03-23 10 views
6

मैं java.lang.String के पैकेज निजी कन्स्ट्रक्टर को प्रतिबिंबित पहुंच प्राप्त करना चाहता हूं।क्या लैम्ब्डा अभिव्यक्तियां उनके दायरे के बाहर कक्षाओं के निजी तरीकों तक पहुंच सकती हैं?

अर्थात्, यह एक:

/* 
* 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 बनाना यह काफी सरल है के लिए, और इसलिए इसे लागू किया जाता है। वही प्रतिबिंब का उपयोग करने के लिए भी सच है।

लेकिन मुझे उत्सुकता है कि क्या कार्यात्मक इंटरफेस के माध्यम से सीधे निर्माता को कॉल करना संभव है।

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) 

है क्यों इस त्रुटि अभी भी फेंक दिया जा रहा है, पहुँच के बावजूद जाहिरा तौर पर दिए जाने:

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 अनुदेश

shared.create("foo".toCharArray(), true) 

निम्न त्रुटि फेंकता है?

क्या कोई भी स्पष्टीकरण के साथ आ सकता है कि जेनरेट किए गए इंटरफ़ेस के पास इस विधि तक पहुंच क्यों नहीं है कि उसके सभी घटकों तक पहुंच है?

क्या कोई समाधान या व्यवहार्य विकल्प है जो वास्तव में शुद्ध प्रतिबिंब या मेथडहैंडल्स पर वापस लौटने के बिना इस विशेष उपयोग मामले के लिए काम करता है?

क्योंकि मैं स्टंप हूं।

+1

निश्चित अपने वास्तविक सवाल का जवाब नहीं कर सकते:

एकमात्र समाधान के लिए लुक-वस्तु String के संदर्भ में रहने वाले और मौजूदा सामान्य interface रों में से एक, बूटस्ट्रैप वर्ग लोडर से सुलभ को लागू करने का उपयोग करने के लिए है , लेकिन शुद्ध प्रतिबिंब यहां भयानक नहीं है। विधि/निर्माता/आदि। उत्पन्न बाइटकोड के साथ एक एक्सेसर के आसपास अनिवार्य रूप से प्रॉक्सी कक्षाएं हैं। उन्हें बनाना एक हेवीवेट ऑपरेशन है, लेकिन एक आमंत्रण नहीं है। 'बुलियन' बॉक्सिंग हो जाता है, लेकिन यह अन्यथा कम से कम सूर्य/ओपनजेडीके कार्यान्वयन में 'नया' का उपयोग करने जैसा ही है। रिफ्लेक्शन एक्सेसर को उत्पन्न करके एक्सेस त्रुटि के आसपास आता है जैसे कि यह उस क्षेत्र के अंदर था जिसके सदस्य सदस्य हैं, जो मुझे लगता है कि LambdaMetaFactory यहां नहीं कर रहा है। – Radiodef

+0

@Radiodef विवरण के लिए धन्यवाद। यदि कोई लैम्ब्डा समाधान नहीं मिल पाता है, तो मैं बस मेथडहैंडल का उपयोग करूंगा। मैं अधिक उत्सुक हूं कि परीक्षा क्यों व्यवहार करती है। –

उत्तर

2

समस्या यह है कि आप देखने वस्तु ओवरराइड पर भरोसा किया जा करने के लिए है, तो String के private विधि इसकी पहुँच देखने प्रक्रिया और लैम्ब्डा मेटा कारखाने पारित करेंगे, लेकिन इसकी अभी भी अपने Test वर्ग के लिए बाध्य कर के रूप में उस वर्ग जो बनाया है MethodHandles.lookup() के माध्यम से लुकअप ऑब्जेक्ट और जेनरेट क्लास एक ही संदर्भ में रहेंगे। जेवीएम इन उत्पन्न वर्गों की पहुंच के दौरान सुलभता के बारे में काफी उदार है, लेकिन स्पष्ट रूप से, आपके अनुप्रयोग वर्ग के संदर्भ में रहने वाले वर्ग से बूटस्ट्रैप वर्ग java.lang.String के private सदस्य को एक्सेस नहीं किया गया है।

आप एक उपयुक्त संदर्भ में रहने वाले लुकअप ऑब्जेक्ट को प्राप्त कर सकते हैं, उदा। MethodHandles.lookup() .in(String.class) (और उसके बाद इसे private या "विश्वसनीय" पहुंच प्राप्त करने के लिए पैच करना), लेकिन फिर, आपको एक और समस्या मिलेगी: java.lang.String (या बस बूटस्ट्रैप लोडर के संदर्भ में) के संदर्भ में रहने वाली कक्षा में आपके कस्टम interface StringCreator तक पहुंच नहीं होगी और इसे लागू नहीं कर सकता।

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

विस्तृत स्पष्टीकरण के लिए धन्यवाद, और एक समाधान समाधान के लिए! मुझे कल्पना नहीं है कि ऐसे लुकअप को बूटस्ट्रैप क्लास लोडर के बाहर कक्षाओं के बारे में जानने का कोई तरीका है? –

संबंधित मुद्दे