Appendix: Java's Lambda

Lambdas are anonymous functions. They were added to Java since 2014 with the release of JDK 8. Lambdas can be used in any place a "Single Abstract Method" (SAM) was used before.

Let's showcase the use of Lambdas through a demo.

public class LambdaDemo {

  public static void main(String[] args) {

  }

  private static List<Course> getSampleCourses() {
    List<Course> courses = new ArrayList<>();
    courses.add(new Course("EN.601.421", "Object-Oriented Software Engineering"));
    courses.add(new Course("EN.601.226", "Data Structures"));
    courses.add(new Course("EN.601.621", "Object-Oriented Software Engineering"));
    return courses;
  }
}

We start with using anonymous inline classes.

private static void usingAnonymousInlineClass() {
  List<Course> courses = getSampleCourses();
  Collections.sort(courses, new Comparator<Course>() {
    @Override
    public int compare(Course c1, Course c2) {
      return c1.getOfferingName().compareTo(c2.getOfferingName());
    }
  });

  for (Course c: courses) {
    System.out.println(c);
  }
}

Call usingAnonymousInlineClass in main and it must print out the sample courses sorted by offeringName.

EN.601.226 Data Structures
EN.601.421 Object-Oriented Software Engineering
EN.601.621 Object-Oriented Software Engineering

Alright, let's do the same thing with a lambda function!

private static void usingLambdaInLongForm() {
  List<Course> courses = getSampleCourses();
  Collections.sort(courses, (Course c1, Course c2) -> {
    return c1.getOfferingName().compareTo(c2.getOfferingName());
  });

  for (Course c: courses) {
    System.out.println(c);
  }
}

Look at the second argument to the Collections.sort method:

(Course c1, Course c2) -> {
    return c1.getName().compareTo(c2.getName());
}

It is a function written in Java's Lambda notation. It is just syntax sugar for anonymous inline implementation of Comparator.

You've used syntax sugar before: this enhanced for loop

for (Course c: courses) {
  System.out.println(c);
}

is syntax sugar for the use of an iterator:

Iterator<Course> it  = courses.iterator();
while (it.hasNext()) {
  Course c = it.next();
  System.out.println(c);
}

Back to Lambdas, let's simplify our lambda expression:

private static void usingLambdaInShortForm() {
  List<Course> courses = getSampleCourses();
  Collections.sort(courses, (c1, c2) -> c1.getOfferingName().compareTo(c2.getOfferingName()));

  courses.forEach(course -> System.out.println(course));
}

Let's focus on the Lambda function:

(c1, c2) -> c1.getOfferingName().compareTo(c2.getOfferingName()));

Notice you don't need to specify the data type of arguments! Moreover, when the body of your lambda function is a single statement, you can eliminate the { } and the return keyword.

Also note the use of the forEach method:

courses.forEach(course -> System.out.println(course));

The forEach is a function that takes a Lambda function as argument.

We came a long way out of our comfort zone; if this is new to you, your head is probably spinning! But since we came so far, let's throw one more ingredient in the mix: Method References.

As you know by now, we use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but calling an existing method. In such a case, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

private static void usingMethodReferences() {
  List<Course> courses = getSampleCourses();
  Collections.sort(courses, Comparator.comparing(Course::getOfferingName));
  courses.forEach(System.out::println);
}

Here is the entire demo program:

Here are some useful links if you want to learn more about Java's Lambda functions: