2016-08-22 80 views
2

我試圖創建一個變量的方法參考,其持有方法名從對象的一些方法:變量法參考8

SomeClass obj = new SomeClass(); 
String methodName = "someMethod"; 

我正在尋找方法來創建準確obj::someMethod,但爲此使用變量methodName。可能嗎?

我知道如何從methodNameobj創建功能接口實例:

() -> { 
    try { 
     return obj.getClass().getMethod(methodName).invoke(obj); 
    } catch (NoSuchMethodException | IllegalAccessException e) { 
     return null; 
    } 
}; 

但我想知道這是可以更簡便方法來完成。

+2

這是一個非常罕見的情況 - Java8語法糖爲此進行了優化的可能性很小。如果這在你的代碼庫中很常見,你可以將它移動到一個輔助方法中,然後執行'() - > Utils.invoke(obj,methodName)'。 –

+0

@OliverCharlesworth建議做什麼是唯一的方法,這可能是一個答案 – Andremoniy

+0

@Andremoniy不僅。還有運行時代碼。 – talex

回答

1

這是一個非常罕見的情況 - Java8語法糖爲此進行了優化的可能性很小。特別是,通常的編譯時類型檢查不可能發生(請記住,方法引用只是符合特定類型合約的匿名類的語法糖)。

如果這種模式在您的代碼庫中很普遍(希望它不是!),您可以將它移動到靜態實用程序方法中,然後執行() -> Utils.invoke(obj, methodName)

1

如果您追求簡潔而非性能,自Java 1.4以來有ExpressionStatement

Object obj="example"; 
String methodName="substring"; 
Object[] arg={ 2, 5 }; 
try { 
    Object result=new Expression(obj, methodName, arg).getValue(); 
    new Statement(System.out, "println", new Object[]{ result }).execute(); 
} catch (Exception ex) { 
    Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex); 
} 

但是,如果你想在標準功能接口,不允許checked異常的情況下使用它們,異常處理將主導源代碼。


可以即使在Java 7中結合一個反射性獲取方法將一個功能接口:

Object obj="example"; 
String methodName="substring"; 
Object[] arg={ 2, 5 }; 
Supplier<String> s; 
Consumer<String> c; 
try { 
    MethodHandle mh=MethodHandles.insertArguments(
     MethodHandles.lookup().bind(obj, methodName, 
      MethodType.methodType(String.class, int.class, int.class)), 
     0, arg); 
    s = MethodHandleProxies.asInterfaceInstance(Supplier.class, mh); 
    mh=MethodHandles.lookup().bind(System.out, "println", 
     MethodType.methodType(void.class, String.class)); 
    c = MethodHandleProxies.asInterfaceInstance(Consumer.class, mh); 
} catch(NoSuchMethodException | IllegalAccessException ex) { 
    Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex); 
    return; 
} 
String result=s.get(); 
c.accept(result); 

這不是短,但避免了在每個功能的評價進行反射查找。


可能更有效方法是使用在Java中8,這是在運行時lambda表達式和方法引用的後端導入的LambdaMetafactory

Object obj="example"; 
String methodName="substring"; 
Object[] arg={ 2, 5 }; 
Supplier<String> s; 
Consumer<String> c; 
try { 
    final MethodHandles.Lookup lookup = MethodHandles.lookup(); 
    MethodHandle mh=lookup.findVirtual(String.class, methodName, 
     MethodType.methodType(String.class, int.class, int.class)); 
    s = (Supplier<String>)LambdaMetafactory.metafactory(lookup, "get", 
     mh.type().changeReturnType(Supplier.class), 
     MethodType.methodType(Object.class), mh, MethodType.methodType(String.class)) 
     .getTarget().bindTo(obj).invokeWithArguments(arg); 
    mh=MethodHandles.lookup().findVirtual(PrintStream.class, "println", 
     MethodType.methodType(void.class, String.class)); 
    c = (Consumer<String>)LambdaMetafactory.metafactory(lookup, "accept", 
     MethodType.methodType(Consumer.class, PrintStream.class), 
     MethodType.methodType(void.class, Object.class), mh, 
     MethodType.methodType(void.class, String.class)) 
     .getTarget().bindTo(System.out).invokeExact(); 
} catch(Throwable ex) { 
    Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex); 
    return; 
} 
String result=s.get(); 
c.accept(result); 

這具有較高創作的複雜性,但由於沒有技術差異不再職能的後續執行會對編譯時方法引用比肩的效率。