52

I have recently mastered the Lambda expression that was introduced in java 8. I find that whenever I am using a functional interface, I tend to always use a Lambda expression instead of creating a class that implements the functional interface.

Is this considered good practice? Or are their situations where using a Lambda for a functional interface is not appropriate?

  • 120
    To the question "Is using technique X whenever possible good practice" the only correct answer is "no, use technique X not whenever possible, but whenever sensible". – Doc Brown Jan 27 '17 at 16:27
  • The choice of when to use a lambda (which is anonymous) versus some other kind of functional implementation (e.g. a method reference) is a really interesting question. I had the opportunity to meet Josh Bloch a couple of days ago, and this was one of the points he talked about. From what he said I understand he's planning to add a new item to the next edition of Effective Java dealing with this exact question. – Daniel Pryden Jan 27 '17 at 22:36
  • @DocBrown That should be an answer, not a comment. – gnasher729 Jan 28 '17 at 23:04
  • 1
    @gnasher729: definitely not. – Doc Brown Jan 29 '17 at 7:51
113

There are a number of criteria that should make you consider not using a lambda:

  • Size The larger a lambda gets, the more difficult it makes it to follow the logic surrounding it.
  • Repetition It's better to create a named function for repeated logic, although it's okay to repeat very simple lambdas that are spread apart.
  • Naming If you can think of a great semantic name, you should use that instead, as it adds a lot of clarity to your code. I'm not talking names like priceIsOver100. x -> x.price > 100 is just as clear as that name. I mean names like isEligibleVoter that replace a long list of conditions.
  • Nesting Nested lambdas are really, really hard to read.

Don't go overboard. Remember, software is easily changed. When in doubt, write it both ways and see which is easier to read.

  • 1
    It is also important to consider the amount of data that is to processed through the lambda, as lambdas are rather inefficient at small amounts of data processing. They really shine in parallelization of large data sets. – CraigR8806 Jan 27 '17 at 18:57
  • 1
    @CraigR8806 I don't think performance is a concern here. Using an anonymous function or creating a class extending the functional interface should be the same performance wise. Performance considerations come in when you talk about using the streams/higher-order-functions api over an imperative style loop, but in this question we are using functional interfaces in both cases just with different syntax. – puhlen Jan 27 '17 at 20:37
  • 16
    "although it's okay to repeat very simple lambdas that are spread apart" Only if they don't necessarily change together. If they must all be the same logic for your code to be correct, you should make them all actually the same method so that changes only need to be made in one place. – jpmc26 Jan 27 '17 at 23:09
  • Overall this is a really good answer. Mentioning the use of a method reference as an alternate to a lambda would patch to only hole I see in this answer. – Morgen Jan 28 '17 at 5:44
  • 3
    When in doubt, writing it both ways and ask someone else who maintains the code which is easier to read. – corsiKa Jan 28 '17 at 8:37
14

It depends. Whenever you find yourself using the same lambda in different places you should consider implementing a class that implements the interface. But if you would've used an anonymous inner class otherwise I think a lambda is far better.

  • 6
    Don't forget the possibility of using a method reference! Oracle has added (and is adding even more in Java 9) static methods and default methods all over the place for exactly that reason. – Jörg W Mittag Jan 27 '17 at 17:57
13

I support Karl Bielefeldt's answer, but want to provide a brief addition.

  • Debugging Some IDE's struggle with scope inside of a lambda, and struggle to display member variables inside the context of a lambda. While hopefully this situation will change down the line, it can be annoying to maintain someone else's code when it is littered with lambdas.
  • Definitely true of .NET. Doesn't make me avoid lambdas, necessarily, but I certainly don't feel any guilt if I do something else instead. – user1172763 Jan 27 '17 at 19:44
  • 3
    If that's the case you're using the wrong IDE. Both IntelliJ and Netbeans do pretty well in that particular area. – David Foerster Jan 28 '17 at 10:57
  • 1
    @user1172763 In Visual Studio, if you want to view member variables, I've found that you can travel up the call stack until you get to the context of the lambda. – Zev Spitz Jan 28 '17 at 18:14
5

Access to local variables of the enclosing scope

The accepted Answer by Karl Bielefeldt is correct. I can add one more distinction:

  • Scope

The lambda code nested inside a method inside a class can access any effectively-final variables found within that method & class.

Creating a class that implements the functional interface does not give you such direct access to the state of the calling code.

To quote the Java Tutorial (emphasis mine):

Like local and anonymous classes, lambda expressions can capture variables; they have the same access to local variables of the enclosing scope. However, unlike local and anonymous classes, lambda expressions do not have any shadowing issues (see Shadowing for more information). Lambda expressions are lexically scoped. This means that they do not inherit any names from a supertype or introduce a new level of scoping. Declarations in a lambda expression are interpreted just as they are in the enclosing environment.

So while there are benefits to pulling out long code and naming it, you must weigh that against the simplicity of direct access to the state of the enclosing method & class.

See:

4

This might be nit-picking, but to all the other excellent points made in other answers, I would add:

Prefer Method References when possible. Compare:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

versus

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

Using a method reference saves you the need to name the lambda's argument(s), which is usually redundant and/or leads to lazy names like e or x.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.