With Java 8 , there have been many welcoming changes in java. This has been the first release after java 5 with so many significant changes. This blog post is inspired from talk from Venkat Subramaniam.

With Java 6,there were no changes in language capabilities but modifications to the JVM. Java 7 introduced the feature to put underscore in numerical literals Hee.Hee. Now the ancient java people can read these numbers better.

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 	3.14_15F;

Lambda Expressions have been a much talked about feature of Java8. But what are lambda expressions and how are they useful to us?

Lambda expressions are anonymous methods aimed at replacing Anonymous Inner Classes in Java.They allow us to do function composition in the same way that we do object composition in java. Lets start with an example.

import java.util.*;
import java.util.function.Consumer;

public class Sample {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    
    // Complex but familiar code. //Self-Inflicted Wound Pattern. 
    for(int i = 0; i < numbers.size(); i++) {
      System.out.println(i);
    }
    
    // Better . Simpler . Both are external iterators. 
    // foreach java5
    for(int e : numbers) {
      System.out.println(e);
    }

    // Internal iterator. Collection , Do the looping , i don't care how.  Really easy to parallelize it. 
    numbers.forEach(
        // Anonymous Inner Classes . Workaround for lack of lambda expressions in java 8. This is good ,
        // But Heck no , to Anonymous Inner Classes. Lots of Ceremony in it.
        /**
        * Represents an operation that accepts a single input argument and returns no
        *  result. Unlike most other functional interfaces, Consumer is expected
        * to operate via side-effects. */
        new Consumer<Integer>() {
            public void accept(Integer number) {
                System.out.println(number);
            }
        }
    );

    // Anonymous function. Lambda Expressions.
    numbers.forEach((Integer number) -> System.out.println(number));
    
    // Optional Type Declaration -- Type Inference -- No need to declare the type of the perimeter.
    numbers.forEach(number -> System.out.println(number));
    
    // Not Complex , But Unfamiliar.
    // Double Colon Operator in Java8. Method References.
    numbers.forEach(System.out::println);
  }
}

Functional interfaces

Functional interfaces have a single functionality to exhibit.There is some common feature among the (java.awt.event.ActionListener, java.util.Comparator, java.util.concurrent.Callable) interfaces and that feature is they have only one method declared in their interface definition.These interfaces are also called Single Abstract Method interfaces (SAM Interfaces). We can use the annotation @FunctionalInterface while defining a functional interface to get compile timer errors in case. Functional Interfaces can have only one abstract methods but multiple default methods are allowed.

Method References

You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, 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.

There are four kinds of method references:

Kind Example
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new

Mapping operation: eliminating mutability

/**
 * A sequence of elements supporting sequential and parallel aggregate
 * operations.  The following example illustrates an aggregate operation using
 * {@link Stream} and {@link IntStream}:*/
import java.util.*;
import java.util.function.*;

public class Streamy {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // Imperative Code . Using Mutations. How Will you make it concurrent for eg. ?
        int totalOfValuesDoubled = 0;
        for(int number : numbers) {
            totalOfValuesDoubled += number * 2;
        }

        System.out.println(totalOfValuesDoubled);

        // Functional Style Code.
        System.out.println(
                numbers.stream()
                        .mapToInt(e -> e*2)
                        .sum());

        // Map-Reduce You Guys , Big Data ... Oooh!!
        System.out.println(
            numbers.stream()
                .map(e -> e*2)
                .reduce(0,(c,e) -> c+e));
    }
}

How do lambda expressions look under the covers.

Are Lambda Expressions under the hood Anonymous Inner Classes . Lets see.

InvokeDynamic . Byte Code . JIT Level Optimization.

  1. invokevirtual
  2. invokestatic
  3. invokeinterface
  4. invokespecial

  5. (New Guy , Say Hello) invokedynamic

invokedynamic is a bytecode instruction that facilitates the implementation of dynamic languages (for the JVM) through dynamic method invocation.

To read more I would refer you to this and this blog.

How are these new methods available on existing interfaces ?

Intefaces now have default methods.

  1. Default Methods are inherited automatically.
  2. You can override a default method.
  3. Methods in the class hierarchy rule.
  4. Collision between interface. Need to resolve it yourself.
interface Fly {
    default void takeOff() { System.out.println("Fly::takeOff"); }
    default void land() { System.out.println("Fly::land"); }
    default void turn() { System.out.println("Fly::turn"); }
    default void cruise() { System.out.println("Fly::cruise"); }
}

interface FastFly extends Fly {
    default void takeOff() { System.out.println("FastFly::takeOff"); }
}

interface Sail {
    default void cruise() { System.out.println("Sail::Cruise"); }
    default void turn() { System.out.println("Sail::turn"); }
}

class Vehicle {
    public void turn() { System.out.println("Vehicle::turn"); }
}

class SeaPlane extends Vehicle implements FastFly, Sail {
    public void cruise() {
        // Need the word super because interfaces can have STATIC methods. 
        FastFly.super.cruise();
        System.out.println("SeaPlane::cruise"); 
    }
}

public class InterfaceExample {

    public static void main(String[] args){
        SeaPlane seaPlane = new SeaPlane();
        seaPlane.takeOff();
        seaPlane.turn();
        seaPlane.cruise();
        seaPlane.land();
    }

}

Do you need abstract classes ? Interfaces are better than abstract classes.

Comparator Interface . Many really awesome default methods.

Read here

Default methods Implementing Strategy pattern

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

class Asset {
    public enum AssetType { STOCK, BOND };

    public final AssetType type;
    public final int value;

    public Asset(AssetType assetType, int assetValue) {
        type = assetType;
        value = assetValue;
    }
}
public class PredicateExample {
    public static void totalAssets(List<Asset> assets, Predicate<Asset> selector) {

        int total = 0;
        for(Asset ass : assets){
            if(selector.test(ass)){
                total += ass.value;
            }
        }

        System.out.println(total);

        /*System.out.println(
                assets.stream()
                        .filter(selector)
                        .mapToInt(asset -> asset.value)
                        .sum()); */
    }

    public static void main(String[] args) {
        List<Asset> assets = Arrays.asList(
                new Asset(Asset.AssetType.STOCK, 100),
                new Asset(Asset.AssetType.BOND, 200),
                new Asset(Asset.AssetType.STOCK, 300),
                new Asset(Asset.AssetType.BOND, 400)
        );


        //We don't want to go this route.
        //totalAssets(assets);
        //totalStockAssets(assets);
        //totalBondAssets(assets);

        totalAssets(assets, asset -> true);
        totalAssets(assets, asset -> asset.type == Asset.AssetType.STOCK);
        totalAssets(assets, asset -> asset.type == Asset.AssetType.BOND);
    }
}

Composing with lambda expressions

import java.util.*;
import java.util.function.Predicate;

public class ComposingLambda {
    public static boolean isGreaterThan2(int number) {
        return number > 2;
    }

    public static boolean isEven(int number) {
        return number % 2 == 0;
    }

    public static int doubleIt(int number) {
        return number * 2;
    }

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3,4,5,6);

        //double the first even number greater than 3 from the list

        System.out.println(
                numbers.stream()
                        // intermediate operation
                        .filter(ComposingLambda::isGreaterThan2)
                        .filter(ComposingLambda::isEven)
                        .mapToInt(ComposingLambda::doubleIt)
                        // short-circuiting terminal operation
                        .findFirst()
                        .orElse(0)
        );
    }
}

Parallel Libraries

A word of Caution:

Consider using S.parallelStream().operation(F) instead of S.stream().operation(F) when operations are independent, and either computationally expensive or applied to many elements of efficiently splittable data structures, or both. In more detail:

  • F, the per-element function (usually a lambda) is independent: the computation for each element does not rely on or impact that of any other element. (See the stream package summary for further guidance about using stateless non-interfering functions.)
  • S, the source collection is efficiently splittable. There are a few other readily parallelizable stream sources besides Collections, for example, java.util.SplittableRandom (for which you can use the stream.parallel() method to parallelize). But most sources based on IO are designed primarily for sequential use.
  • The total time to execute the sequential version exceeds a minimum threshold. These days, the threshold is roughly (within a factor of ten of) 100 microseconds across most platforms. You don’t need to measure this precisely though. You can estimate this well enough in practice by multiplying N (the number of elements) by Q (cost per element of F), in turn guestimating Q as the number of operations or lines of code, and then checking that N * Q is at least 10000. (If you are feeling cowardly, add another zero or two.) So when F is a tiny function like x -> x + 1, then it would require N >= 10000 elements for parallel execution to be worthwhile. And conversely, when F is a massive computation like finding the best next move in a chess game, the Q factor is so high that N doesn’t matter so long as the collection is completely splittable.

Why They are not Suitable for Commercial Applications

Future-ology –> Async Programming !!

Java Threads are very useful but sometimes you wish that a thread could return some value that we can use. Java 5 introduced **java.util.concurrent.Callable interface in concurrency package that is similar to Runnable interface but it can return any Object and able to throw Exception.

import java.util.*;
import java.util.concurrent.*;

public class FutureExample implements Callable<String> {

    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        //return the thread name executing this callable task
        return Thread.currentThread().getName();
    }

    public static void main(String args[]){
        //Get ExecutorService from Executors utility class, thread pool size is 10
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //create a list to hold the Future object associated with Callable
        List<Future<String>> list = new ArrayList<>();
        //Create MyCallable instance
        Callable<String> callable = new FutureExample();
        for(int i=0; i< 100; i++){
            //submit Callable tasks to be executed by thread pool
            Future<String> future = executor.submit(callable);
            //add Future to the list, we can get return value using Future
            list.add(future);
        }
        for(Future<String> fut : list){
            try {
                //print the return value of Future, notice the output delay in console
                // because Future.get() waits for task to get completed
                System.out.println(new Date()+ "::"+fut.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        //shut down the executor service now
        executor.shutdown();
    }

}

Problem with Futures

A Future represents the pending result of an asynchronous computation. It offers a method — get — that returns the result of the computation when it’s done. The problem is that a call to get is blocking until the computation is done. This is quite restrictive and can quickly make the asynchronous computation pointless.

Beside implementing the Future interface, CompletableFuture also implements the CompletionStage interface. A CompletionStage is a promise. It promises that the computation eventually will be done. The great thing about the CompletionStage is that it offers a vast selection of methods that let you attach callbacks that will be executed on completion. This way we can build systems in a non-blocking fashion.

supplyAsync takes a Supplier containing the code we want to execute asynchronously — in our case the sendMsg method. thenAccept is one of many ways to add a callback. It takes a Consumer — in our case notify — which handles the result of the preceding computation when it’s done. If you want to continue passing values from one callback to another, thenAccept won’t cut it since Consumer doesn’t return anything.

To keep passing values, you can simply use thenApply instead.

thenApply takes a Function which accepts a value, but also return one.

To avoid callback HELL , use thenCompose

thenCombine combines two future

Completable Futures in Java 8

Jeff is chillin' at the bar. Suddenly his ex walks by....
_/﹋\_
(_)?
<,,>
_/﹋\_
Jeff don't like her. There's only one thing to do...
_/﹋\_
(ಠ益ಠ)
<,︻╦╤─ ҉ - - ---
_/﹋\_