2017-03-18 11 views
0

ラムダの名前を付けると便利なことがあります。特にそれらをパラメータとして渡すとき。デバッグ用のネーミング(toString)ラムダ式

A本当に簡単な例では、舞台裏で素晴らしいパフォーマンスの最適化を失うことなく、ラムダに名前を付けるJVMによっていくつかの機能を持っていいだろう

public class Main { 
    public static void main(String[] args) { 
     Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty()); 
     maybePrint("Hello", p); 
     maybePrint(" ", p); 
    } 

    static <T> void maybePrint(T s, Predicate<T> pred) { 
     if (pred.test(s)) { 
      System.out.println(s.toString()); 
     } else { 
      System.err.println(pred + " says no to \"" + s + "\""); 
     } 
    } 
} 

です。このような

Somethinkは私のために大丈夫だと思う:

Predicate<String> p = nameIt("isNotEmpty", (s) -> !s.trim().isEmpty()); 
+0

メソッド参照は 'named'のようなものですlambda – Turo

+0

メソッド参照に基づくラムダは実行時にデバッグ可能なStringを持っていません。 toString結果もデバッガ(少なくともeclipse)ビューでもありません。 – Sebastian

+0

あなたは正しいと思うが、今は時々それを得ることができることを学んだ[Java 8メソッド参照のMethodInfoを取得する方法は? ](http://stackoverflow.com/questions/19845213/how-to-get-the-methodinfo-of-a-java-8-method-reference)。私はちょうど仕事に興味があったのですか? – Turo

答えて

0

これが問題のために私の解決策(https://stackoverflow.com/a/23705160/1325574でandersschullerの溶液からインスピレーションを得た)です。この実装がうまくいかないコーナーケース(クラスローディング)があるかもしれませんが、最も単純なケースでは機能します。

私は私の限られたJMHた知識でこれの小さなパフォーマンス・テストを作成しました: https://gist.github.com/picpromusic/4b19c718bec5a652731a65c7720ac5f8

-resultsが@stuartmarks Naming(toString) Lambda-Expressions for Debugging purpose

# Run complete. Total time: 00:40:31 

Benchmark      Mode Cnt   Score   Error Units 
MyBenchmark.testNamedPredicate thrpt 200 45938970,625 ± 615390,483 ops/s 
MyBenchmark.testPredicate  thrpt 200 23062083,641 ± 154933,675 ops/s 
MyBenchmark.testPredicateReal thrpt 200 48308347,165 ± 395810,356 ops/s 
MyBenchmark.testToString  thrpt 200 138366708,182 ± 1177786,195 ops/s 
MyBenchmark.testToStringNamed thrpt 200 252872229,907 ± 8044289,516 ops/s 
MyBenchmark.testToStringReal thrpt 200 6670148,202 ± 40200,984 ops/s 

の答えの実現のために測定され、「名前」あなたが見ることができるように、それは無名のラムダを使用するよりも約2倍遅くなります。ですので、-DnamedLambdasEnabled = trueを設定する際は注意してください。私にとっては、Real-lambdaでtoStringを呼び出すのは驚くほど高価です。たぶん誰かがそれを説明することができます、または私のjmhテストは馬鹿です。ここで

はコードです:

/** 
* Helper Class to give lambda a name ("toString") for debugging purpose 
* 
*/ 
public class LambdaNamer { 

    private static Method TO_STRING; 

    static { 
     try { 
      TO_STRING = Object.class.getMethod("toString"); 
     } catch (NoSuchMethodException | SecurityException e) { 
      throw new RuntimeException("There is something rotten in state of denmark!"); 
     } 
    } 

    /** 
    * Overrides toString "Method" for a given lambda. 
    * 
    * @param name toString result of lambda 
    * @param obj the lambda to encapsulate 
    * @return the named lambda 
    */ 
    public static <T> T nameIt(String name, T obj) { 
     if (Boolean.getBoolean("namedLambdasEnabled")) { 
      Class<T> clazz = (Class<T>) obj.getClass(); 
      Class<?>[] interfaces = clazz.getInterfaces(); 

      return (T) Proxy.newProxyInstance(// 
        obj.getClass().getClassLoader(),// 
        interfaces, // 
        (Object proxy, Method method, Object[] args) -> { 
         if (TO_STRING.equals(method)) { 
          return name; 
         } else { 
          return method.invoke(obj, args); 
         } 
        }); 
     } else { 
      return obj; 
     } 
    } 
} 

あなたは他のソリューションを持っていますか?パフォーマンスに影響がないかもしれない何か?ここで

1

が頭に浮かぶの代替です:

static <T> Predicate<T> nameIt(String name, Predicate<? super T> pred) { 
    return new Predicate<T>() { 
     public String toString() { return name; } 
     public boolean test(T t) { return pred.test(t); } 
    }; 
} 

これは非常に簡単と思われます。私はそれをベンチマークしていませんが、かなり速くすべきだと思われます。単一のオブジェクトと1つのメソッド呼び出しを追加し、ボクシング/アンボックスのオーバーヘッドを回避します。

欠点は、名前付きインスタンスを提供するすべての機能インターフェイスに対して、このような小さな関数を記述する必要があることです。

+0

はい、答えはhttp://stackoverflow.com/questions/23704355/creating-string-representation-of-lambda-expression/23705160#23705160これは高速ですが、すべてのラムダには一般的ではない1つのソリューションです。私は私の答えを更新し、この答えのベンチマーク結果を含めます。 – Sebastian