2015-07-02 9 views
6

मैं allowing अंदरमैं Java8 विधि संदर्भ का लक्ष्य कैसे ढूंढ सकता हूं?

public interface Service { 
    public String stringify(Object o); 
} 
service = mockery.mock(Service.class); 
mockery.allowing(service::stringify).with(42).will(() -> "42"); 

तो एक नकली वस्तु के लिए कॉल कैप्चर करना चाहते हैं मैं एक Function<Object, String>

है वहाँ किसी भी reflecto-जादू मुझे विधि संदर्भ से बनाए गए समारोह से सेवा मिल जाएँगे जो है ?

public WithClause allowing(Function<T,R> f) { 
    Object myServiceBackAgain = findTargetOf(function); 
    .... 
} 

मुझे पता है कि फ़ंक्शन हमेशा इन विधि संदर्भों से आ जाएगा, इसलिए मुझे जितना आवश्यक हो उतना नीचे डालना अच्छा लगता है।

यह संबंधित Is it possible to convert method reference to MethodHandle? जैसा ही प्रश्न नहीं है, क्योंकि शुरुआत के लिए यह एक ही प्रश्न नहीं है, केवल संबंधित क्षेत्र में। और यहां तक ​​कि अगर मैं एक विधि हैडल प्राप्त कर सकता हूं, तो मुझे इसका लक्ष्य नहीं मिल सकता है।

+0

कोई भी नहीं कहा कि यह वही सवाल है। शायद आपको संदेश पढ़ना चाहिए: "* इस प्रश्न में पहले से ही एक जवाब है *"। तो * उत्तर * पढ़ने शुरू करें। यह सबकुछ बताता है, इसलिए आपको यह अनुमान लगाने की आवश्यकता नहीं है कि क्या आप मेथडहैंडल प्राप्त कर सकते हैं, यह बात नहीं है। यह * केवल एक शीर्षक * है। – Holger

+0

शायद @ होल्गर, शायद इस प्रश्न का उत्तर के रूप में, आप यह बताना चाहते हैं कि लिंक किए गए प्रश्न का उत्तर मेरा जवाब कैसे देता है? विशेष रूप से जहां समारोह धारावाहिक नहीं है। –

+0

मूल रूप से यह समझाता है कि यह संभव नहीं है (सीरियलाइजेशन चाल के अलावा) और यहां तक ​​कि यदि यह भी था, तो यह वही नहीं किया गया जो आप उम्मीद करते हैं क्योंकि इस बात की कोई गारंटी नहीं है कि आपके स्रोत कोड में कोई विधि संदर्भ बाइट पर प्रत्यक्ष हैंडल हो रहा है कोड स्तर ध्यान दें, अतिरिक्त जानकारी के लिए संबंधित प्रश्नों के बहुत सारे लिंक हैं। यही कारण है कि मैंने सीधे इस सवाल को निर्देश दिया कि "जावा 8 विधि संदर्भ का तरीका कैसे प्राप्त करें?"] (Http://stackoverflow.com/q/19845213/2711488) जो आपके शब्द के करीब है लेकिन प्रदान करता है उत्तरों में कम जानकारी। – Holger

उत्तर

4

this SO post से चाल का उपयोग करके आप लक्ष्य पा सकते हैं। नीचे दी गई महत्वपूर्ण विधि findTarget है। जैसा कि यह पता चला है, लैम्बडा वास्तव में अपने लक्ष्यों को पकड़ते हैं, और आप उन्हें SerializedLambda से एक्सेस कर सकते हैं।

हालांकि, यह एक बहुत ही बुरा प्रतिबिंब हैक है और यह भविष्य के संस्करणों में तोड़ने की संभावना है। मैं इसका उपयोग नहीं करता हूं।

import java.io.Serializable; 
import java.lang.invoke.SerializedLambda; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.Optional; 
import java.util.function.Function; 

public class FindMethodReferenceTarget { 
    public static void main(String[] args) { 
    String s = "123"; 
    Optional<Object> target = findTarget(s::charAt); 
    System.out.println(target.get().equals(s)); 

    Object o = new FindMethodReferenceTarget(); 
    target = findTarget(o::equals); 
    System.out.println(target.get().equals(o)); 
    } 

    private static <T, R> Optional<Object> findTarget(
     DebuggableFunction<T, R> methodReference) { 
    return getLambda(methodReference).map(l -> l.getCapturedArg(0)); 
    } 

    private static Optional<SerializedLambda> getLambda(Serializable lambda) { 
    for (Class<?> cl = lambda.getClass(); cl != null; cl = cl.getSuperclass()) { 
     try { 
     Method m = cl.getDeclaredMethod("writeReplace"); 
     m.setAccessible(true); 
     Object replacement = m.invoke(lambda); 
     if (!(replacement instanceof SerializedLambda)) { 
      break; // custom interface implementation 
     } 
     SerializedLambda l = (SerializedLambda) replacement; 
     return Optional.of(l); 
     } catch (NoSuchMethodException e) { 
     // do nothing 
     } catch (IllegalAccessException | InvocationTargetException e) { 
     break; 
     } 
    } 

    return Optional.empty(); 
    } 

    @FunctionalInterface 
    private static interface DebuggableFunction<T, R> extends 
     Serializable, 
     Function<T, R> {} 
} 
0

लक्ष्य को खोजने का कोई सीधा तरीका नहीं है, क्योंकि विधि संदर्भों को कवर के तहत लैम्बडास (जो परिभाषा, अज्ञात द्वारा) में अनुवादित किया जाता है। तो आपको एक वर्कअराउंड का उपयोग करने की आवश्यकता होगी।

संभवतः आप जावा 7 के प्रॉक्सी से परिचित हैं, क्योंकि आपने अपनी mock फैक्ट्री विधि को कार्यान्वित करने में कामयाब रहे हैं।

तब कामकाज यह है कि जब कोई आपके allowing विधि को आमंत्रित करता है, तो आप अपने सभी मैक्स को सतर्क करने के लिए कुछ प्रकार का वैश्विक ध्वज सेट करते हैं, जिसे आप अगली कॉल रिकॉर्ड करना चाहते हैं, और फिर आप दिए गए लैम्ब्डा को आमंत्रित करते हैं। यह देखते हुए कि किस मॉक ने कॉल रिकॉर्ड किया है, अब आपको विधि संदर्भ का लक्ष्य मिला है, और आप वैश्विक ध्वज को अनसेट कर सकते हैं और अपने बाकी मॉकिंग फ्रेमवर्क के साथ आगे बढ़ सकते हैं।

यह बदसूरत है, मुझे पता है।

+0

हाँ, यही वह क्षण है जो मैं कर रहा हूं! मैं अभी भी उम्मीद कर रहा हूं कि गुमनाम केवल तभी लागू होता है जब आप नहीं जानते कि नीचे क्या करना है - इंटरफ़ेस के सभी कार्यान्वयन के बाद एक ठोस चीज है जिसे इसे लक्षित करना चाहिए। –

+0

@DuncanMcGregor जरूरी नहीं है; JVM लैम्बडा को बंद करने के रूप में कार्यान्वित कर सकता है (और कुछ ऑब्जेक्ट पर कोई विधि नहीं) जहां रिसीवर के रूप में कार्यरत 'सेवा' का उदाहरण अनिवार्य रूप से लैम्बडा के कार्यान्वयन के परिप्रेक्ष्य से स्थानीय चर है (और कोई रास्ता नहीं है उस समारोह के बाहर से मनमाने ढंग से कार्य के स्थानीय चर का आत्मनिरीक्षण करने के लिए)। सबसे बुरे मामले में, फ़ंक्शन जावा भी नहीं हो सकता है बल्कि इसके बजाय मूल (उदा। सी) कोड पर कॉल हो सकता है। मुझे नहीं पता कि जेवीएम वास्तव में ऐसा करता है, लेकिन मुझे लगता है कि उन्होंने एपीआई/अनुबंध स्थापित किया है ताकि वे ऐसा कर सकें। –

+0

मैं मानता हूं कि लैम्ब्डा को लागू करने के कई तरीके हैं - लेकिन इस मामले में, कॉल करने के लिए JVM को ऑब्जेक्ट का संदर्भ रखना चाहिए? –

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