2016-08-22 11 views
2

の変数メソッド参照にこのために変数methodNameを使用します。出来ますか?私はオブジェクトからいくつかのメソッドのメソッド名を保持する変数とメソッドの参照を作成しようとしていたJava 8

私は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); 
} 

しかし、あなたがチェック例外を許可しない標準関数インタフェースの文脈でそれらを使用したい場合は、例外処理は、ソースコードを支配します。


は、あなたも、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に導入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); 

これは、より高い創造の複雑さを持っていますが、技術的な差はもはや存在しないような機能のその後の実行は、コンパイル時のメソッド参照と同等の効率を持つことになります。

関連する問題