55

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?

SteelToe
  • 1,539
  • 3
  • 13
  • 22
  • 125
    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
  • 3
    @gnasher729: definitely not. – Doc Brown Jan 29 '17 at 07:51

6 Answers6

122

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.

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
  • 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
  • 2
    @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
  • 18
    "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 05: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 08:37
  • @CraigR8806 Not sure how it's done in Java, but in .NET the lambda is compiled to an actual method, which means that there is no difference in performance. – Zev Spitz Jan 28 '17 at 18:11
  • "Nested lambdas are really, really hard to read" I believe this is true only when you nest one-statement lambdas with no braces. If braces are used, then an IDE that highlights the closing counterpart of every parenthesis/bracket can help you track where does each lambda start and end. – The SE I loved is dead Jan 28 '17 at 19:35
  • I agree with everything here except one thing: "software is easily changed". No, READABLE software is easily changed. Every criteria here is about choosing the more readable option, which is dead on. Software should be easy to change. That isn't the same as being easy to write. Which is why you ask a fellow programmer to tell you how easy it is to read. If you want to know how readable something is the worst programmer in the world to ask is the one who wrote it. So don't just "write it both ways and see". Get both way's peer reviewed. – candied_orange Jan 29 '17 at 15:33
  • since lamda exists my collegues use them everywhere, you have 200-600 line lambda here and there, it is hell – Heetola Mar 07 '20 at 05:55
17

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.
Jolleyboy
  • 279
  • 1
  • 5
  • 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
15

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.

Tohnmeister
  • 514
  • 2
  • 4
  • 7
    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
7

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:

Basil Bourque
  • 1,000
  • 5
  • 10
5

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.

Matt McHenry
  • 169
  • 3
0

Building on Matt McHenry's answer, there can be a relationship between lambda and method references where the lambda can be re-written as a series of method references. For example:

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

vs

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