Join David Gassner for an in-depth discussion in this video Using built-in functional interfaces with lambdas, part of Java SE 8 New Features.
- View Offline
I've previously described how to use a lambda expression to implement an interface that you've created yourself. Now, I'll show lambda expressions with built in interfaces. Interfaces that are a part of the Java runtime. I'll use two examples. I'm working in a project called BuiltInInterfaces, that's a part of the exercise files. And I'll start with this class. UseRunnable. In this class, I'm implementing the Runnable interface. This is an interface that's a part of the multithreaded architecture of Java.
In order to create a thread, you create an instance of the Runnable interface which has a single abstract method named run. I have two instances of the interface here, and I'm passing them to two instances of the thread class and calling the start method. I'll run the code a few times. Because these threads are running simultaneously, the question of which ends first is arbitrary. It depends on internals of the Java runtime. The first time I run it, thread two might end first. The second time thread one and so on.
But my focus here is on how you code, not in how it operates. I'm going to show how to use lambda expressions to replace these inner classes. I'll comment out the code that's declaring the two objects. And then I'll place the cursor down here and re-declare them and do the implementation with lambdas. I'll start with Runnable and I'll name this one r1, and then, after the assignment operator I'll put in a pair of parentheses. This matches the pair of parentheses with no arguments that's declared in the run method in the more verbose version.
Then I'll add the arrow token and then I'll copy the one functional line of code that outputs to the console and I'll paste it right here. And that's all I need. Now I'll do duplicate that line of code and for the second one I'll change it to r2 and change the output accordingly. I'll save the change and run the code. The behavior is exactly the same, using lambda expression in this context doesn't give you any performance benefit.
It's a simple way of reducing the amount of code you have to write. If you compare the two versions, the one with the inner classes took about ten lines of code. And the one with lambdas took two. It's more readable, and it's more maintainable. If you need to expand any of these methods to multiple lines, wrap them with braces. For example, I want to add a call to the sleep method to cause the first thread to pause for a moment. So, I'll place a starting brace after the token and when I press Enter or Return, Eclipse adds the ending brace for me.
And then I'll add the semi colon at the end of the brace to complete the statement. Now, I can add any code I want between the braces. I'll call thread.sleep. Now, when I press the period, I see this Problems During Content Assist dialog. You might see this a few times as you code using the new version of the Java Developer tools. It's a bit of a bug. And you can disable this feature as described in the dialogue, but I just ignore it. I'll click OK, and then I'll type the beginning of the method I want to use, sleep.
And I'll use the version of the method that accepts a single long value. And I'll pass in a value of 1,000. That means, pause this thread for 1,000 milliseconds, or one second. I see an error token, over on the left. And when I float the mouse over it, it shows me there's an un-handled exception. So, I'll add a tri-catch block, around the call to the sleep method. I'll press Ctrl+1 for a quick fix. Or Cmd+1 on Mac. And choose surround with tri catch.
I'll get rid of this automatically generated comment. And then I'll save the changes and I'll once again run the code. This time, I see clearly that the second thread completes before the first. Because the first thread now has that one second pause. I'll click it again and see exactly the same result. So, lambda expressions can be used either with single lines of code or with complete code blocks. And you can use them effectively with the built-in interfaces with either single lines of code or with complete code blocks.
Let's look at another example. I'll close this file and open the file UseComparator.Java. In this Java class, I've created an array list of strings. Some are upper case, and some are lower case. Then, at lines 19 to 24, I'm calling the collections classes' static sort method. That sorts the array collection, and it does it with case sensitivity by default. Then I'm looping through the collection and outputting the strings to the console.
In the next bit of code, I'm using a comparator. The comparator is another functional interface in Java which has a single abstract method. This method is the compare method. It accepts two arguments. The type of the arguments depends on the generic declaration here. If you say that the comparator is for strings then the arguments will be strings. This is an instance of an anonymous inner class. I'm not naming the object, I'm declaring it and passing it into the sort method, all in one bit of code.
The compare method calls the string object's compare to ignore case method and passes in the second string object. It returns an integer. The integer value can be either negative 1, 0, or positive 1, depending on the results of the comparison. And that value is used by the sort method to figure out how to sort the values. Because I'm explicitly using the compareToIgnoreCase method, this will be a case insensitive sort. Here's the result of the current code.
The case sensitive sort, shown at the beginning, sorts all of the uppercase values to the top. And the lowercase values to the bottom. The case insensitive sort, done with the comparator object, does an accurate case insensitive sort. So, everything is purely alphabetical. Now, our goal is to learn about lambda expressions and this bit of code would work fine in earlier versions of Java too, but let's see how it might look with the lambda expression. I'll place the cursor before the call to the Sort method, the version that takes two arguments.
The collection and the comparator. Now I'll code up a comparator using a lambda expression. I'll choose the comparator interface and I'll set its generic type to string and I'll name it comp. Now, because the compare method takes two arguments, I need to pass those arguments in here. I'll pass them in, and name them str1, and str2. Then I'll add the arrow token, or lambda operator and a pair of braces. I'll add the semicolon after the second brace.
And now I'll take this bit of code, that's the code that's providing the functionality. And cut and paste it and place it in the lambda expression. Then, I'll come down here and I'll remove the code that's declaring the anonymous object. And I'll replace it with the comp object that I just declared with the lambda. And there's the refactored result. The functionality is exactly the same. When I use the second sort method and the comparator object it results in a case insensitive, purely alphabetical sort operation but the code is simpler, more concise and easier to read.
As before, it doesn't provide you any performance benefit. The underlying functionality is exactly the same. Whether you declare your own classes, use inner or anonymous inner classes, or lambda expressions, is completely up to you.
- Installing Java SE 8
- Working with lambda expressions and method references
- Traversing collections with streams
- Calculating timespans with the new DateTime API