2016-01-21 28 views
6

Java 8: clousure ile Metot örneğine lambda dönüştürmek dahil

Ben ile kullanım için bir lambda ifadesi için bir Method örneğini almak istiyorum (Bu sonuçlar tüm "yöntemi referans" hakkında çünkü arama zordur) eski yansıma tabanlı API. Klous dahil edilmeli, böylece thatMethod.invoke(null, ...) numaralı telefonun aranması lambdayı çağırmakla aynı etkiye sahip olmalıdır.

MethodHandles.Lookup'a baktım, ancak yalnızca ters dönüş için alakalı görünüyor. Ama sanırım bind metodu, klozu dahil etmede yardımcı olabilir mi?

Düzenleme:

ben am lambda experssion olduğunu varsayalım:

Function<String, String> sayHello = name -> "Hello, " + name; 

ve ben eski bir çerçeveye sahip (SpEL) bir API vardır böyle

registerFunction(String name, Method method) 

verilen arayacak hangi this numaralı argümana sahip Method (yani, yöntemin statik olduğu varsayılmıştır). Bu yüzden lambda mantığı + kefaret verisini içeren özel bir Method örneğini almam gerekecek.

+0

Bir lambda ifadesi için bir "Yöntem" örneği gibi bir şey yoktur. Lambda, anonim işlevler için sözdizimsel şekerdir. Size bir örnek verir, ancak bir yöntem yapamazsınız. – Jatin

+0

@Jatin Açıkçası, sentezlenmiş fonksiyonel arabirim impelementasyonu hala normal yöntemlerle normal bir nesnedir ve normal yansıma erişimini kullanabilirsiniz. Ben sadece klousürün nasıl sarılacağını merak ediyorum. –

cevap

7

Zarif bir yol bulamazsanız, çirkin bir yoldur (Ideone). yansıma katılır Olağan uyarı: Gelecek sürümlerde vb

public static void main(String[] args) throws Exception { 
    Function<String, String> sayHello = name -> "Hello, " + name; 
    Method m = getMethodFromLambda(sayHello); 
    registerFunction("World", m); 
} 

static void registerFunction(String name, Method method) throws Exception { 
    String result = (String) method.invoke(null, name); 
    System.out.println("result = " + result); 
} 

private static Method getMethodFromLambda(Function<String, String> lambda) throws Exception { 
    Constructor<?> c = Method.class.getDeclaredConstructors()[0]; 
    c.setAccessible(true); 
    Method m = (Method) c.newInstance(null, null, null, null, null, 0, 0, null, null, null, null); 
    m.setAccessible(true); //sets override field to true 

    //m.methodAccessor = new LambdaAccessor(...) 
    Field ma = Method.class.getDeclaredField("methodAccessor"); 
    ma.setAccessible(true); 
    ma.set(m, new LambdaAccessor(array -> lambda.apply((String) array[0]))); 

    return m; 
} 

static class LambdaAccessor implements MethodAccessor { 
    private final Function<Object[], Object> lambda; 
    public LambdaAccessor(Function<Object[], Object> lambda) { 
    this.lambda = lambda; 
    } 

    @Override public Object invoke(Object o, Object[] os) { 
    return lambda.apply(os); 
    } 
} 
+3

Bu harikulade hackeryeri için sizi selamlıyorum! –

+0

"MethodHandle" mülkündeki olasılıkları araştırdım ve yapabileceği en iyi şey, örnek yöntemlerini üretmek, bu yüzden dinamik gereksinim nedeniyle bir çıkmaz sokak gibi görünüyor. –

+1

En komik kısım, 'm' erişilebilir hale getirme biçimidir; "setAccessible (true)" işlevini çağırmak yerine, backend alanını "geçersiz kıl" ı, "true" olarak ayarlamak için daha sonra kullanmak üzere "Alan" seçeneğinde "setAccessible (true)" işlevini çağırmak için "geçersiz kıl" ı elde edersiniz. Sanırım, "setAccessible (true)" işlevini çok sık kullanırsanız, bu ne olur? – Holger

4

Eh, lambda ifadeleri derleme sırasında yöntemlerine şekeri alınmış ve yeter ki this yakalamak yok gibi (erişmezseniz olmayan static üyelerin bozulabilir), bu yöntemler static olacaktır. Zor olan bölüm, fonksiyonel arayüz örneği ve hedef yöntemi arasında denetlenebilir bir bağlantı olmadığı için bu yöntemlere ulaşmaktır.

burada, en basit durumda bu göstermek için: Yalnızca bir lambda ifadesi ve dolayısıyla tek aday yöntemi olmadığı için

public class LambdaToMethod { 
    public static void legacyCaller(Object arg, Method m) { 
     System.out.println("calling Method \""+m.getName()+"\" reflectively"); 
     try { 
      m.invoke(null, arg); 
     } catch(ReflectiveOperationException ex) { 
      ex.printStackTrace(); 
     } 
    } 
    public static void main(String[] args) throws URISyntaxException 
    { 
     Consumer<String> consumer=s -> System.out.println("lambda called with "+s); 
     for(Method m: LambdaToMethod.class.getDeclaredMethods()) 
      if(m.isSynthetic() && m.getName().contains("lambda")) { 
       legacyCaller("a string", m); 
       break; 
      } 
    } 
} 

Bu sorunsuz çalışır. Bununla birlikte, bu işleri

static Method lambdaToMethod(Serializable lambda) { 
    for(Class<?> cl=lambda.getClass(); cl!=null; cl=cl.getSuperclass()) try { 
     Method m=cl.getDeclaredMethod("writeReplace"); 
     m.setAccessible(true); 
     try { 
      SerializedLambda sl=(SerializedLambda)m.invoke(lambda); 
      return LambdaToMethod.class.getDeclaredMethod(sl.getImplMethodName(), 
       MethodType.fromMethodDescriptorString(sl.getImplMethodSignature(), 
        LambdaToMethod.class.getClassLoader()).parameterArray()); 
     } catch(ReflectiveOperationException ex) { 
      throw new RuntimeException(ex); 
     } 
    } catch(NoSuchMethodException ex){} 
    throw new AssertionError(); 
} 
public static void main(String[] args) 
{ 
    legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable) 
     s -> System.out.println("first lambda called with "+s))); 
    legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable) 
     s -> System.out.println("second lambda called with "+s))); 
} 

: bu yöntem adı derleyici spesifiktir ve bazı seri numaraları veya karma kodlarını içerebilir vb kludge üzerinde

lambda ifade seri hale ve seri formu kontrol etmektir , seri hale getirilebilir lambdalar yüksek bir fiyata geliyor.


basit çözüm, bununla birlikte, şu anda, javac doğru ek açıklama depolamaz, yöntemlerine göre ilerlerken bulunabilir lambda ifade parametreye bir ek açıklama da bu konuda this question görmek olacaktır konu.


Ama aynı zamanda dikkate alabilir sadece kodu yerine lambda ifadesi tutan sıradan static calışmalarımız.Bir yöntem için Method nesnesinin alınması, düz-ileriye doğrudur ve yöntem referanslarını kullanarak bunların dışında bir işlevsel arabirim örneği oluşturabilirsiniz.