2016-01-04 24 views
10

की संलग्न कक्षा प्राप्त करें मेरे पास एक विधि है जो कार्यात्मक पैरामीटर लेती है, उदा। एक Runnable। चूंकि यह एक लाइब्रेरी विधि है, मैं इसे कार्यात्मक पैरामीटर से व्युत्पन्न लॉगर का उपयोग करना चाहता हूं। कार्यात्मक पैरामीटर पर getClass कॉलिंग सामान्य कक्षाओं के लिए ठीक काम करता है, और मैं नेस्टेड या अज्ञात कक्षाओं के लिए getEnclosingClass कर सकता हूं;जावा लैम्ब्डा अभिव्यक्ति

Class<?> type = runnable.getClass(); 
String canonical = type.getCanonicalName(); 
int lambdaOffset = canonical.indexOf("$$Lambda$"); 
if (lambdaOffset > 0) { 
    try { 
     type = Class.forName(canonical.substring(0, lambdaOffset)); 
    } catch (ClassNotFoundException e) { 
     // strange, but we can stick to the type we already have 
    } 
} 

आप देख सकते हैं, कि बहुत ही सुंदर और शायद पोर्टेबल नहीं नहीं है: लेकिन अगर यह एक लैम्ब्डा अभिव्यक्ति है, यह कुछ अस्पष्ट नाम $$Lambda$ है, जो मैं इस तरह मैन्युअल रूप से बंद पट्टी सकता युक्त देता है। मैंने getEnclosingClass, getEnclosingMethod, और getEnclosingConstructor की कोशिश की है, लेकिन वे सभी null लौटते हैं।

कोई विचार?

+1

मेरा मानना ​​है कि इस डिजाइन कर रहा है। –

उत्तर

0

मैं benjiweber द्वारा एक शांत समाधान मिल गया। यह एक java.lang.invoke.SerializedLambda को lamda serializing करने पर निर्भर करता है और फिर उसके घोषित वर्ग मिलती है:

private static final int COUNT = 1_000_000; 
private static boolean first = true; 

public static void main(String[] args) { 
    long t = System.currentTimeMillis(); 
    for (int i = 0; i < COUNT; i++) { 
     showIdentity(() -> { 
     }); 
    } 
    String time = NumberFormat.getNumberInstance().format((double) (System.currentTimeMillis() - t)/COUNT); 
    System.out.println("time per call: " + time + "ms"); 
} 

public interface MethodAwareRunnable extends Runnable, Serializable {} 

private static void showIdentity(MethodAwareRunnable consumer) { 
    consumer.run(); 
    String name = name(consumer); 
    if (first) { 
     first = false; 
     Class<?> clazz = consumer.getClass(); 
     System.out.printf("class name  : %s%n", clazz.getName()); 
     System.out.printf("class hashcode : %s%n", clazz.hashCode()); 
     System.out.printf("canonical name : %s%n", clazz.getCanonicalName()); 
     System.out.printf("enclosing class: %s%n", clazz.getEnclosingClass()); 
     System.out.printf("lambda name : %s%n", name); 
    } 
} 

private static String name(Object consumer) { 
    return method(consumer).getDeclaringClass().getName(); 
} 

private static SerializedLambda serialized(Object lambda) { 
    try { 
     Method writeMethod = lambda.getClass().getDeclaredMethod("writeReplace"); 
     writeMethod.setAccessible(true); 
     return (SerializedLambda) writeMethod.invoke(lambda); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

private static Class<?> getContainingClass(SerializedLambda lambda) { 
    try { 
     String className = lambda.getImplClass().replaceAll("/", "."); 
     return Class.forName(className); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

private static Method method(Object lambda) { 
    SerializedLambda serialized = serialized(lambda); 
    Class<?> containingClass = getContainingClass(serialized); 
    return Arrays.stream(containingClass.getDeclaredMethods()) 
       .filter(method -> Objects.equals(method.getName(), serialized.getImplMethodName())) 
       .findFirst() 
       .orElseThrow(RuntimeException::new); 
} 

इस कोड का एक बहुत कुछ है, लेकिन भूमि के ऊपर मेरी मशीन है, जो सबसे उपयोग के मामलों के लिए ठीक है पर 0.003 के बारे में एमएस है।

और आप अन्य बिंदास सामग्री कर सकते हैं जैसे:

Map<String, String> hash = hash(
    hello -> "world", 
    bob -> "was here" 
); 
+0

आप यह सब सामान केवल लैम्ब्डा के संलग्न 'कक्षा' के लिए क्यों करते हैं? शायद आपका लॉगर कुछ मिलता है 'getLogger (clazz.getName()) '। तो क्यों अपने शुरुआती दृष्टिकोण का उपयोग नहीं कर रहे हैं और वर्ग नाम भाग '$$ Lambda *' तक लेते हैं? – SubOptimal

+0

@SubOptimal: आप सही हैं। यही वह है जो मैंने पहले ही तय किया है; मैंने अभी यह जोड़ा क्योंकि यह इतना उबर-कूल है ;-) –

+0

मेरे लिए यह केवल उपयोगी होगा यदि लैम्ब्डा वर्ग का नाम एक साधारण तरीके से नहीं लिया जा सका। लेकिन यह इस सरल पैटर्न का पालन करें 'the.package.EnclosingClass $$ Lambda $ xx/yyyy'। जहां 'xx' प्रति वर्ग अनुक्रमिक संख्या है और 'yyy' Lambda का हैशकोड है। हमेशा की तरह, समाधान उस चीज़ पर निर्भर करता है जिसे आप प्राप्त करना चाहते हैं। – SubOptimal

4

जैसा कि पहले से ही Tassos Bassoukos द्वारा वर्णित है, यह डिज़ाइन द्वारा है।

लैम्ब्डा (कक्षा) के लिए बाइटकोड रनटाइम पर उत्पन्न होता है। तो आपको कक्षा का वास्तविक नाम क्या मिलता है। और नाम target class name + "$$Lambda$" + a counter के रूप में उत्पन्न होता है।

प्रदर्शन के लिए एक छोटा सा स्निपेट खोजें।

package sub.optimal; 
import static java.lang.System.out; 

public class EnclosingClass { 

    static class InnerRunnable implements Runnable { 

     @Override 
     public void run() { 
      out.println("--- inner class"); 
     } 
    } 

    public static void main(String... args) { 
     showIdentity(() -> System.out.println("--- lambda 1")); 
     showIdentity(() -> System.out.println("--- lambda 2")); 
     showIdentity(new InnerRunnable()); 
     showIdentity(new Runnable() { 
      @Override 
      public void run() { 
       out.println("--- anonymous class"); 
      } 
     }); 
    } 

    private static void showIdentity(Runnable runnable) { 
     runnable.run(); 
     Class<? extends Runnable> clazz = runnable.getClass(); 
     out.printf("class name  : %s%n", clazz.getName()); 
     out.printf("class hashcode : %s%n", clazz.hashCode()); 
     out.printf("canonical name : %s%n", clazz.getCanonicalName()); 
     out.printf("enclosing class: %s%n", clazz.getEnclosingClass()); 
     out.println(); 
    } 
} 

उत्पादन

--- lambda 1 
class name  : sub.optimal.EnclosingClass$$Lambda$1/2147972 
class hashcode : 2147972 
canonical name : sub.optimal.EnclosingClass$$Lambda$1/2147972 
enclosing class: null 

--- lambda 2 
class name  : sub.optimal.EnclosingClass$$Lambda$2/10376386 
class hashcode : 10376386 
canonical name : sub.optimal.EnclosingClass$$Lambda$2/10376386 
enclosing class: null 

--- inner class 
class name  : sub.optimal.EnclosingClass$InnerRunnable 
class hashcode : 28014437 
canonical name : sub.optimal.EnclosingClass.InnerRunnable 
enclosing class: class sub.optimal.EnclosingClass 

--- anonymous class 
class name  : sub.optimal.EnclosingClass$1 
class hashcode : 19451386 
canonical name : null 
enclosing class: class sub.optimal.EnclosingClass 
संबंधित मुद्दे