Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I log a human-readable string for a lambda?

I will try to be very careful here with my wording, as a lambda is essentially just a method signature and body.

The premise for my question is that it would be very useful to be able to keep the concise syntax lambda, and to be able to have a human-readable name (string) to identify it, i.e. when you log it out.

Previously, with anonymous classes I could always override toString.

final Runnable runnable = new Runnable() {  
  @Override public void run() { System.out.println("1"); }
  @Override public String toString() { return "anonymous-1"; }
}

Scenarios

The following are scenarios where I have found myself needing to revert back to code to be able to deciper

  1. One usage scenario for this is where you have a lambda argument to another function, and in your logs you want to be able to print out the specific lambda / implementation that is being passed in. Say, for example you had a Consumer that took a String, and you had implementations of this that would print out a a greeting plus the string in a number of language. It would be good to name the strategies, say "in french", "in spanish".
  2. You have a Set of Runnable say, each to be applied in turn, and you want to log out the contents of that set.

The problem As there is no accompanying toString then all you get in the logs when you try to log out the lambda reference, for example:

MyClass$$Lambda$1/424058530@1c20c684

Approaches

  1. I suppose you could always have a reference to a logger in the lambda and capture that, such that when the lambda is executed you can print a static string to identify that lambda.
  2. Forego lambda goodness, and revert back to anonymous classes... can't have the cake and eat it, so to say.

Appreciate people's thoughts and experiences in and around this.

like image 394
E Cheung Avatar asked Mar 15 '23 11:03

E Cheung


1 Answers

Here's a middle ground that is a little more syntactically compact that just going back to inner classes -- a Runnable-with-toString factory.

static Runnable makeRunnable(Runnable r, Supplier<String> toStringFn) {
    return new Runnable() {
        public void run() { r.run(); }
        public String toString() { return toStringFn.get(); }
    };
}

Now, at the use site, you do:

Runnable r = makeRunnable(() -> System.out.println("1"), 
                          () ->  "anonymous-1");

(If you have no reason to believe that the toString method will need access to any state, you can just turn the second argument into String instead of Supplier<String>.)

like image 70
Brian Goetz Avatar answered Mar 19 '23 10:03

Brian Goetz