Java 8: Gangnam Style Lambda Functions

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.

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.

LambdaFunctionExamples.zip