Practical Functional Java

Lambda Syntax

Jeff Butler

https://github.com/jeffgbutler/practical-functional-java

https://jeffgbutler.github.io/practical-functional-java/

Lambdas

Lambda == Anonymous Class

All code examples in examples.basics.lambdas.LambdaTest.java

Test Objects
          
    private List<SimpleImmutableObjectWithBuilder> getTestObjects() {
        List<SimpleImmutableObjectWithBuilder> answer = new ArrayList<>();
        
        answer.add(SimpleImmutableObjectWithBuilder.of(33, "Oak Tree"));
        answer.add(SimpleImmutableObjectWithBuilder.of(22, "Elm Tree"));
        answer.add(SimpleImmutableObjectWithBuilder.of(11, "Ash Tree"));
        
        return answer;
    }
          
        

Persistent Comparator (All Java Versions)

          
    public void testPersistentComparator() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort(new TestComparator());
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
    
    private static class TestComparator implements
            Comparator<SimpleImmutableObjectWithBuilder> {
        @Override
        public int compare(SimpleImmutableObjectWithBuilder o1,
                SimpleImmutableObjectWithBuilder o2) {
            return o1.getId().compareTo(o2.getId());
        }
    }
          
        

Anonymous Class (All Java Versions)

          
    public void testAnonymousClass() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort(new Comparator<SimpleImmutableObjectWithBuilder>() {
            @Override
            public int compare(SimpleImmutableObjectWithBuilder o1,
                    SimpleImmutableObjectWithBuilder o2) {
                return o1.getId().compareTo(o2.getId());
            }
        });
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
          
        

Lambda Syntax 1 - Concise (Java 8+)

          
    public void testConciseLambdaSyntax() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort((o1, o2) -> o1.getId().compareTo(o2.getId()));
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
          
        

Lambda Syntax 2 - Verbose (Java 8+)

          
    public void testVerboseLambdaSyntax() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort((o1, o2) -> {
            return o1.getId().compareTo(o2.getId());
        });
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
          
        

Lambda Syntax 3 - Very Verbose (Java 8+)

          
    public void testVeryVerboseLambdaSyntax() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort((SimpleImmutableObjectWithBuilder o1,
                SimpleImmutableObjectWithBuilder o2) -> {
            return o1.getId().compareTo(o2.getId());
        });
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
          
        

Method Reference (Java 8+)

          
    public void testMethodReference() {
        List<SimpleImmutableObjectWithBuilder> testObjects = getTestObjects();
        
        testObjects.sort(this::compare);
        
        assertThat(testObjects.get(0).getDescription()).isEqualTo("Ash Tree");
        assertThat(testObjects.get(1).getDescription()).isEqualTo("Elm Tree");
        assertThat(testObjects.get(2).getDescription()).isEqualTo("Oak Tree");
    }
    
    private int compare(SimpleImmutableObjectWithBuilder o1,
            SimpleImmutableObjectWithBuilder o2) {
        return o1.getId().compareTo(o2.getId());
    }
          
        

Method references can reference:

  • Static methods (String::valueOf)
  • Instance methods (this::someMethod in some cases)
  • Instance methods (SomeObject::someMethod in some cases)
  • Constructors (SomeObject::new)

How Does this Work?

  • Functional Interfaces
  • Type Inference

Functional Interfaces

  • Interface with one, and only one, abstract method (not including equals, toString, etc. from Object)
  • May or may not be annotated with @FunctionalInterface
  • May have other static and default methods - no restriction

With a functional interface, the compiler can infer parameter and output types.

Comparator is a Functional Interface
          
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);

    // many many default and static methods
}

// List's sort method takes a Comparator.  The compiler can infer everything it needs
public interface List<E> extends Collection<E> {

    default void sort(Comparator<? super E> c) {
       ...   
    }
}
          
        

Summary

  • All of these options do the same thing!
  • My preference is method reference if possible
  • Second best is the concise lambda syntax
  • Why? Clean Code! Both lead you towards smaller pure methods.