Thursday 31 July 2014

JAVA 8 - PART1

I was just started looking Java8 and was impressed with the features it has.

Bought the book 'Java 8 IN ACTION' , which is very good to start with.

I would share some important points from this book and add the more information as and when I get time to read this.

Let's Start

LAMBDA EXPRESSIONS
It’s an Anonymous function that can be passed around: it doesn’t have a name, but it has a list of parameters, a body, a return type, and also possibly a list of exceptions that can be thrown

Good and precise way to represent Behaviour Parameterisation

BEFORE:
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
AFTER (WITH LAMBDA EXPRESSIONS):
Comparator<Apple>  byWeight  = 
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

With Type inference - No explicit type on the parameters a1,a2
Comparator<Apple>  byWeight  = 
(a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

Syntax:
(parameters) -> expression

or (note the curly braces for statements)

(parameters) -> { statements; }



Has to be used in conjunction with Functional Interfaces.
To demonstrate, for ex- we have the below process method which accepts Runnable Functional Interface
   public void process(Runnable r) {
        r.run();
    }

Various ways we can call using Lambda expressions... Old Style
  Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Using Anonymous class");
            }
        };
        process(r1);
        Runnable r2 = () -> System.out.println("Using Lambda Expressions");
        process(r2);

        process(() -> System.out.println("With Lambda Expression Passed Directly"));

a functional interface is an interface that specifies exactly one abstract method, which are annotated with @FunctionalInterface
Ex:- Comparator, Runnable, Callable, Predicate, Consumer, Function

The signature of the abstract method of the functional interface essentially describes the
signature of the lambda expression. We call this abstract method a function descriptor.

Ex:- Runnable – Functional Interface
      run()  - Function Descriptor




Listing 1 - Filter method which takes the List and a Predicate, which the filters the list based on the type of Predicate it has passed.
 public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {  
     List<T> result = new ArrayList<>();  
     for (T e : list) {  
       if(predicate.test(e)) {  
         result.add(e);  
       }  
     }  
     return result;  
   }  
Here's some examples to call the filter:
Filter by Apple color
  List blueapples = filter(getAppleInventory(), (Apple apple) -> "blue".equalsIgnoreCase(apple.getColor()));

Filter by Even numbers:
  List evenNums = filter(getNumbers(), (Integer i) -> i % 2 == 0);


Predicate nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List nonEmpt = filter(listOfStrings, nonEmptyStringPredicate);

Using Consumer functional Interface
public interface Consumer<T>{  
   public void accept(T t);  
 } 
You might use this interface when you need to access an object of type T and perform some operations on it.
public static <T> void forEach(List<T> list, Consumer<T> consumer) {  
     for (T i : list)  
       consumer.accept(i);  
   }
we can call forEach using Lambda expressions... - The below will compute the sum of the integerList
   private static int sum = 0;
   forEach(integerList, (Integer i) -> {       
            sum = sum + i;
            System.out.println("Sum :: " + sum);
         });
Using Function functional Interface
public interface Function<T, R>{  
   public R apply(T t);  
 } 
You might use this interface when you need to define a lambda that can extract information from the input object (for example, extracting the weight of an apple) or transform the input (for example, a string to its length).
   public static <T,R> List<R> map(List<T> list, Function<T,R> function) {  
     List<R> resultType = new ArrayList<>();  
     for (T s : list)  
       resultType.add(function.apply(s));  
     return resultType;  
   }  
we can call map() using Lambda expressions to transform the output from String to Integer...
 List<Integer> integerList = map(Arrays.asList("convert", "String", "to", "respective", "Integer", "length"), (String s) -> s.length());


Method References
 Method references let you reuse existing method definitions and pass them just like lambdas.

BEFORE:
appleList.sort((Apple a1, Apple a2)
-> a1.getWeight().compareTo(a2.getWeight()));

AFTER (USING A METHOD REFERENCE AND JAVA.UTIL.COMPARATORS.COMPARING):
appleList.sort(comparing(Apple::getWeight));

For example, Apple::getWeight is a method reference to getWeight() defined in the Apple
class. It can be seen as a shorthand for the lambda expression (Apple a) ->
a.getWeight().

RECIPE FOR CONSTRUCTING METHOD REFERENCES
There are three main kinds of method references.
1. A method reference to a static method (for example, the method parseInt of Integer,
written Integer::parseInt)
2. A method reference to an instance method of an arbitrary type (for example, the
method length of a String, written String::length)
3. A method reference to an instance method of a specific object (for example, suppose
you have an object expensiveTransaction from class Transaction with an instance
method getValue(), you can write expensiveTransaction::getValue)

Composing Comparators

Reverse order - to sort in decreasing weight
appleList.sort(comparing(Apple :: getWeight).reverse());

Chaining Comparators - multiple Comparators to further refine the comparisions
appleList.sort(comparing(Apple :: getWeight)
                      .reverse()
                      .thenComparing(Apple :: getCountry));


Composing Functions
The Function interfaces comes with two default methods: andThen and compose that both
return an instance of Function.

The method andThen returns a function that applies a given function first to an input, and
then applies another function to the result of that application.
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); 
int result = h.apply(1);  // 4

The method compose will first apply the function given as argument to compose and then apply the function to the result. It works opposite to andThen
Function<Integer, Integer> h = f.compose(g); 
int result = h.apply(1); // 3


In the next part, I will cover another interesting feature 'Processing Data with Streams'



No comments:

Post a Comment