In my last post, I showed how lambda expressions can improve your code and introduced the Predicate interface. This post focuses on how the Function interface can be used in Java.
The java.util.function Package
Of course, Predicate is not the only functional interface provided with Java 8. There are a number of standard interfaces designed as a starter set for developers.
Predicate-- a property of the object passed as argumentConsumer-- an action to be performed with the object passed as argumentFunction-- transform a T to a USupplier-- provide an instance of a T (such as a factory)UnaryOperator-- a unary operator from T -> TBinaryOperator-- a binary operator from (T, T) -> T
In addition to these interfaces, there are primitive versions of many of these as well. This should give you a great starting point for using lambda expressions.
Gangnam Style Names and Function
When working on the previous example, I decided it would be nice to have a flexible printing system for the Person class. One feature requirement is to display names in both a western style and an Asia style. In the west, names are displayed with the given name first and the surname second. In many Asian cultures, including Korea, names are displayed with the surname first and the given name second. In honor of Psy's very fun video, the Asian name display style will be called "Gangnam" style.
An Old Style Example
Here is an quick old style example of how I might have implemented a Person printing class without lambda.
PersonWriterOld.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Function;
5
6 /**
7 * @author MikeW
8 */
9 public class PersonWriterOld {
10
11
12 public void writeShortWesternName(List<Person> pl){
13
14 System.out.println("=== Short Western List ===");
15
16 for (Person p:pl){
17 System.out.println("\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
18 "EMail: " + p.getEmail() + "\n" +
19 "Phone: " + p.getPhone());
20 }
21
22 }
23
24 public void writeShortGangnamName(List<Person> pl){
25
26 System.out.println("=== Short Gangnam List ===");
27
28 for (Person p:pl){
29 System.out.println("\nName: " + p.getSurName() + " " + p.getGivenName() + "\n" +
30 "EMail: " + p.getEmail() + "\n" +
31 "Phone: " + p.getPhone());
32 }
33
34 }
35
36 public void writeFullWesternName(List<Person> pl){
37
38 System.out.println("=== Full Western List ===");
39
40 for (Person p:pl){
41 System.out.println("\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
42 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
43 "EMail: " + p.getEmail() + "\n" +
44 "Phone: " + p.getPhone() + "\n" +
45 "Address: " + p.getAddress());
46 }
47
48 }
49
50
51 public void writeFullGangnamName(List<Person> pl){
52
53 System.out.println("=== Full Gangnam List ===");
54
55 for (Person p:pl){
56 System.out.println("\nName: " + p.getSurName() + " " + p.getGivenName() + "\n" +
57 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
58 "EMail: " + p.getEmail() + "\n" +
59 "Phone: " + p.getPhone() + "\n" +
60 "Address: " + p.getAddress());
61 }
62
63 }
64
65 }
Note the repetitive pattern. We have a separate method and loop for each style of printing.
The Function Interface
The Function interface is perfect for this problem. Its only method apply has the following signature.
public R apply(T t){ }
So it takes a generic class T and returns a generic class R. So for this example, pass the Person class and return a String. A rewrite of the PersonWriter class yields the following:
PersonWriterNew.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Function;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class PersonWriterNew {
11
12 public void printLambdaList(List<Person> pl, Function<Person,String> f, String description){
13 System.out.println(description);
14 for (Person p:pl){
15 System.out.println(f.apply(p));
16 }
17 }
18
19 }
Now that is quite a bit simpler. A List, a Function, and the title for the list is passed to a single method. The apply method prints each Person based on what was passed in.
So how are the Functions defined? Here is the test code which calls the previous class.
NameTestNew.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Function;
5
6 /**
7 * @author MikeW
8 */
9 public class NameTestNew {
10
11 public static void main(String[] args) {
12
13 System.out.println("\n==== NameTestNew02 ===");
14
15 List<Person> list1 = Person.createShortList();
16 PersonWriterNew pw = new PersonWriterNew();
17
18 // Define Lambda Functions
19 Function<Person, String> shortWestern = p -> {
20 return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
21 "EMail: " + p.getEmail() + "\n" +
22 "Phone: " + p.getPhone();
23 };
24
25
26 Function<Person, String> shortGangnam = p -> "\nName: " +
27 p.getSurName() + " " + p.getGivenName() + "\n" +
28 "EMail: " + p.getEmail() + "\n" +
29 "Phone: " + p.getPhone();
30
31
32 Function<Person, String> fullWestern = p -> {
33 return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
34 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
35 "EMail: " + p.getEmail() + "\n" +
36 "Phone: " + p.getPhone() + "\n" +
37 "Address: " + p.getAddress();
38 };
39
40 Function<Person, String> fullGangnam = p -> "\nName: " + p.getSurName() + " "
41 + p.getGivenName() + "\n" + "Age: " + p.getAge() + " " +
42 "Gender: " + p.getGender() + "\n" +
43 "EMail: " + p.getEmail() + "\n" +
44 "Phone: " + p.getPhone() + "\n" +
45 "Address: " + p.getAddress();
46
47 // print list
48 pw.printLambdaList(list1, shortWestern, "\n==== Short Western Style ====");
49 pw.printLambdaList(list1, fullGangnam, "\n==== Full Gangnam Style ====");
50
51 }
52 }
Each print behavior is assigned to a Function. Note how the Person and String classed are part of the variable defintion. Also, examine the examples closely. Notice that the printing code can be defined as a lambda expression statement or as a block with a return statement.
The lambda expressions could very easily be incorporated into a Map making their reuse much easier.
Sample Output
Here is some sample output from the program.
==== Short Gangnam Style ==== Name: Baker Bob EMail: bob.baker@example.com Phone: 201-121-4678 Name: Doe Jane EMail: jane.doe@example.com Phone: 202-123-4678 Name: Doe John EMail: john.doe@example.com Phone: 202-123-4678 Name: Johnson James EMail: james.johnson@example.com Phone: 333-456-1233 Name: Bailey Joe EMail: joebob.bailey@example.com Phone: 112-111-1111 Name: Smith Phil EMail: phil.smith@examp;e.com Phone: 222-33-1234 Name: Jones Betty EMail: betty.jones@example.com Phone: 211-33-1234
Resources
The NetBeans project and all the course code is included in the following zip file.