Hey, let's continue the Java 8 series!!!

This post is the third subject about the changes that you can find between version 6 to 8. Here I will talk about the Lambda.

Summary

So... what is the Lambda expression?

It's a new concept of function as the value. The lambda expression is used in many languages that follow the Functional Programming paradigm. It is an anonymous function. Java is able, for example, to store a method in a variable and pass a method as a parameter. In other words: behaviour parameterization. A big advantage is a concise representation.

 It is an anonymous function that allows you to pass methods as arguments or simply, a mechanism that helps you remove a lot of boilerplate code.

The syntax:

  • parameters + arrow + body: (args1, args2, args3, ……) -> { body }
  • Parameter:
    • Quantity: zero or more
      • Zero: () -> System.out.println("Hi");
      • One parameter: the parentheses is not mandatory
        • s -> System.out.println(s);
    • Type: can be declared explicitly, or it can be inferred from the context
      • (s) -> System.out.println(s);
      • (String s) -> System.out.println(s);
  • Body
    • Quantity: one or more statements
    • One statement: curly brackets are not required
    • Return:
      • it is optional if the body doesn't return a result.
      • If the return keyword is used, curly brackets are required.

Lambda Expression vs Anonymous Inner Class

Lambda Anonymous

Lambda

  • It implements a functional interface
  • Lambdas can only access final or effectively final variable
  • It is not a scope of its own but is part of the enclosing scope. Lambda can access instance or static variables of the enclosing class.
  • It is an invokedynamic instruction. This allows dynamic languages to bind to symbols at runtime.
    • Lambda is compiled and converted into a private method of the class. 
  • Performance: it is pure compile-time activity and doesn’t incur extra cost during runtime.

Anonymous Inner Class

  • It can extend a class or implement an interface with any number of methods
  • It can use instance variables and thus can have state
  • it has scope for variable defined inside the inner class (it's a class).
  • It compiles to a class
  • Performance: It requires class loading, memory allocation, object initialization and invocation of a non-static method. So lambda has better performance than anonymous inner classes.

PS> These differences were completely extracted from [1] [2]

Tips

  • Lambda can only throw the same type or a subtype checked exceptions specified in the throws clause of the functional interface method.
  • Lambda cannot access the default methods of a functional interface.
  • It does not allow passing a constant value as a parameter.
  • If thisis used in the lambda, it refers to the enclosing class where the expression is written.
  • The type of a lambda is deduced from the context
  • Lambda expressions can be used in:
    • A variable declaration
    • An assignment
    • A return statement
    • An array initializer
    • As a method or constructor arguments
    • A ternary conditional expression
    • A cast expression

 


Reference: Java In Action (GitHub) - No Lambda




Reference: Java In Action (GitHub) - Using Lambda


  Use it if you …

  • "are encapsulating a single unit of behavior that you want to pass to other code." 
  • "need a simple instance of a functional interface and none of the preceding criteria apply".


Functional Interface 

It is an interface that specifies only one abstract method and zero or more default methods. Comparator and Runnable are examples of functional interfaces because the first one has only compare method and the second one the run method. The lambda expression can be considered an instance of the concrete implementation of the functional interface.

Runnable runnable = ()->System.out.println("Executing run method!");
void process(Runnable r){r.run();}
process(runnable);
process(()->System.out.println("Other way!!!"));
  • The @FunctionalInterface annotation can be used to identify a functional interface. It is optional, however, it is a good practice. If the annotation is used and the interface is not a valid functional interface, it will generate a compile-time error.
  • Function descriptor identify the signature of the abstract method of a functional interface.
  • Java library has new functional interfaces inside the java.util.function package (Predicate, Consumer, Function, Supplier, UnaryOperator, BinaryOperator, BiPredicate, BiConsumer, BiFunction).


Java Built-in Lambda Interface

This interface was created to cover the most common scenarios usages.  The letters  T and U represent parameter types and R the return type.

Functional Interface: Supplier;               Function Descriptor:   () -> T

# it takes no arguments and only returns some value

String t = "One";
Supplier<String> supplierStr = () -> t.toUpperCase();
System.out.println(supplierStr.get());

# Boxing and Unboxing:
BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier

# This interface doesn't define default methods.

Functional Interface: Predicate; Function Descriptor: T->boolean

# It is often used when filtering or matching
(check a condition and return a boolean value)

public static String process(List<T> list, Predicate<T> p) {
  if(p.test(list)){return "It's Empty");}
  else {return "It's NOT Empty");}
}
Predicate<String> notEmpty = (String s) -> s.isEmpty();
String result = process(listOfStrings, notEmpty);

# Boxing and Unboxing:
IntPredicate, LongPredicate, DoublePredicate

# <a href="http://ocpj8.javastudyguide.com/ch10.html" >Default methods</a>
default Predicate<T> and (Predicate<? super T> other)
default Predicate<T> or (Predicate<? super T> other)
default Predicate<T> negate()

# static method
static <T> Predicate<T> isEqual(Object targetRef)

Functional Interface: BiPredicate<L, R>;   Function Descriptor:     (L,R) -> boolean

# It's the predicate with two arguments

BiPredicate<Integer, Integer> divisible = (t, u) -> t % u == 0;
boolean result = divisible.test(10, 5);

# Default methods is the same of predicate, but with two arguments

Functional Interface: Consumer;            Function Descriptor: T -> void

# It accepts a single input argument and returns no result;

public static void process(List<T> list, Consumer<T> c) {
   c.accept(list.get(0));
}
process(Arrays.asList(1,2), (Integer i)->System.out.println(i));

# Boxing and Unboxing:
IntConsumer, LongConsumer, DoubleConsumer

# Default method
default Consumer<T> andThen(Consumer<? super T> after)

Functional Interface: BiConsumer<T, U>;  Function Descriptor:    (T, U) -> void

# It's the consumer with two arguments

BiConsumer<String, String> cStr =
      (t, u)->System.out.println(t + " " + u);
cStr.accept("Hi", "there");

# Boxing and Unboxing:
ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>

# Default methods is the same of consumer, but with two arguments

Functional Interface: Function<T, R>;         Function Descriptor: T -> R

# It's is used for turning one parameter into a value of a potentially different
type and returning it.

public <T,R> List<R> process(List<T> list, Function<T, R> f) {
   List<R> result = new ArrayList<>();
   result.add(f.apply(list.get(0)));
   return result;
}
List<Integer> list =
    process(Arrays.asList("teste","2"), (String s)->s.length());

# Boxing and Unboxing:
# returns a generic type and takes a primitive argument
IntFunction<R>, LongFunction<R>, DoubleFunction<R>

# returns a primitive type and takes a generic argument
ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T>
// ToDoubleFunction, ToLongFunction
ToIntFunction<String> i = (x) -> Integer.parseInt(x);
System.out.println(i.applyAsInt("2"));

# takes a primitive argument and returns another primitive type
// IntToLongFunction, LongToDoubleFunction, LongToIntFunction, IntToDoubleFunction
IntToLongFunction itl = (x) -> Long.MAX_VALUE - x;
System.out.println(itl.applyAsLong(2));

# Default methods
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)

Functional Interface: UnaryOperatorFunction Descriptor:     T -> T

# It's a special case of a function. They require all type parameters to be the same type
UnaryOperator<String> up = (x)-> x.toUpperCase();
System.out.println(up.apply("teste"));

# Boxing and Unboxing:
IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator

#Default value
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
static <T> UnaryOperator<T> identity()

Functional Interface:  BiFunction<T, U, R>Function Descriptor:    (T, U) -> R

# It's the function with two arguments

round(5.4, 3.8, (d1, d2) -> Math.round(d1, d2));

# Boxing and Unboxing:
ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U>

# Default method
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)

Functional Interface: BinaryOperator</strong>;  Function Descriptor:    (T, T) -> T

# It's a specialization of the <code>BiFunction</code> interface.

# Used when the arguments and the result are of the same type.

BinaryOperator<String> binOp = (t, u) -> t.concat(u);
System.out.println(binOp.apply("Hello", " there"));

# Boxing and Unboxing:
IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator

# Default method is inherited from BiFunction

# Static methods
static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator)

static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator)

The complete code examples you can see in “Java In Action” github - Lambda and OCP8 - Lambda Expression.


Return Optional

The type Option was created to represent concepts like "not knowing" or "not applicable". It can be empty or it can have some value. It will help a lot the old problems with null value. Optional is a clear statement and allow the use of functional programming style.

# Old version
Optional o = (value== null) ? Optional.empty(): Optional.of(value);

# Using Optional
Optional o = Optional.ofNullable(value);

#Other examples
Optional.empty()
Optional.ofNullable(nullValue)
Optional.of(value)
Optional.of(value).get()
Optional.ofNullable(nullValue).orElse(5)
Optional.of(value).orElse(5)
Optional.of(value).isPresent()

# Other methods
ifPresent(Consumer c)
 - opt.ifPresent(System.out::println)
orElseGet(Supplier s)
 - opt.orElseGet(() -> Math.random())
orElseThrow(Supplier s)
 - opt.orElseThrow(() -> new IllegalStateException())

Optional example you can find in github/fabiana2611


Method Reference

It is similar to lambda, but it can be clearer. You can use this using (1) Class::staticMethod with no arguments (If exists arguments it is passed automatically behind the curtain); (2) with an instance method of an existent object; and (3) with the constructor. Good complete examples you can see in JavaInAction and OCP8.

# Example 1
- Lambda: inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
- Reference: inventory.sort(comparing(Apple::getWeight));

# Example 2
- Lambda: (str, i) -> str.substring(i)
- Reference: String::substring

# Example 3 (existent object)
- Lambda: (x,y) -> obj.getK();
- Reference: obj::getK

# Example 4 (constructor - ClassName::new)
- Lambda: ()->new Integer();
- Reference: Integer::new
or with arguments
- Lambda: (a)->new Integer(a);
- Reference:
     Function<Integer, Integer> i = Integer::new;
     Integer i2 = i.apply(2);

Related Posts

References