Java 8 New Feature with examples part 1

You will learn how to use default interface methods, static interface method, lambda expressions, diamond problem, method references and repeatable annotations etch. At the end of the article you will be familiar with the most recent java 8 API changes like streams, functional interfaces, map extensions and the new Date API.

1. Default Methods for Interfaces.

Java 8 enables us to add non-abstract method implementations to interfaces by using the default keyword. This feature is also known as Extension Methods.

Example of default method.

interface  Java8DefaultI
{
    abstract void hello();
    default void helloWorld(){
        System.out.println("Java8Default method calling !!");
    }
}

Along with abstract method "hello"  in Interface we have default method "helloWorld". Concrete class only need to implement the abstract method and default method will be used as it.

public class Java8Default {
    public static void main(String[] args) {
        Java8DefaultI java8Default = new Java8DefaultI() {
            @Override
            public void hello() {
                System.out.println("Abstract method Hello Calling !!");
            }
        };
        java8Default.hello();
        java8Default.helloWorld();
    }
}

Output :

Abstract method Hello Calling !! Java8Default method calling !!


2. Static Methods for Interfaces:

Java 8 enables us static method  to interfaces by using the static keyword. Static Methods in Interface are those methods, which are defined in the interface with the keyword static. These static methods contain the complete definition of the function and since the definition is complete and the method is static, therefore these methods cannot be overridden or changed in the implementation class.

Example of static method : In below example define and declared the static method in interface and show here to to call this.


interface Java8StaticI{

    abstract void hello();
    static void helloWorld(){
        System.out.println("Java8 Static method calling !!");
    }
}

Along with abstract method "hello"  in Interface we have static method "helloWorld". Concrete class only need to implement the abstract method and static method will not be override be used as it.


public class Java8Static {
    public static void main(String[] args) {
        Java8StaticI java8Static=new Java8StaticI(){
            @Override
            public void hello() {
                System.out.println("Hello method calling..");
            }
        };
        // Non-static method call by object created
        java8Static.hello();
        // Static method called by Interface name
        Java8StaticI.helloWorld();
    }
}
// Output

Hello method calling.. Java8 Static method calling !!


  • 3. Functional Interfaces.

In Java 8, a functional interfaces was introduced. A so called functional interface must contain exactly one abstract method declaration. Each lambda expression of that type will be matched to this abstract method. Since default methods are not abstract you are free to add default methods to your functional interface.

Example: To ensure that your interface meet the requirements, you should add the @FunctionalInterface annotation. Conceptually, a functional interface has exactly one abstract method. The compiler is aware of this annotation and throws a compiler error as soon as you try to add a second abstract method declaration to the interface.


@FunctionalInterface
interface Java8FunctionalI<FT>{
    T convert(F form);
}

public class Java8Functional
{
    public static void main(String[] args) {
           Java8FunctionalI<String,Integerconverter=(form)->Integer.valueOf(form);
           Integer intV=converter.convert("221");
           System.out.println(intV);

    }
}
// Output
221


4. Lambda Expressions.

A basic example of the Lambda Expression is: (x,y) -> x+y;

A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.

Example: Lambda expression provides implementation of functional interface.  Take an example to sort the array list using The static utility method Collections.sort accept a list and comparator in order to sort the elements of the given list. Often we create the anonymous method.

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Java8Lambda1 {
    public static void main(String[] args) {

        List<Stringnames = Arrays.asList("ram""shyam""geeta""anita");

        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String aString b) {
                return a.compareTo(b);
            }
        });

        names.stream().forEach(System.out::println);
    }
}


Above example of how to sort a list of strings in prior versions of Java 8

Instead of creating anonymous objects, Java 8 comes with a much shorter syntax using Lambda Expression.

Collections.sort(names,(String a, String b) ->{
                return a.compareTo(b);
    });

For one line method bodies you can skip both the braces {} and the return keyword. But it gets even more shorter:

Collections.sort(names,(String a, String b) ->a.compareTo(b));

The java compiler is aware of the parameter types so you can skip them as well. gets even more shorter:

Collections.sort(names,(a, b) ->a.compareTo(b));


5. Lambda Scopes:

Accessing outer scope variables from lambda expressions is very similar to anonymous objects. You can access final variables from the local outer scope as well as instance fields and static variables.

Accessing local variables: We can read final local variables from the outer scope of lambda expressions:


public class Java8LambdaScope {

    public static void main(String[] args) {
        final double pie=3.14;
        LambdaScope lambdaScope=(x)->pie*x*x;
        double area=lambdaScope.areaOfCircle(5);

        System.out.println(area);

    }
}


If we don't declare variable as a final it is also valid. The variable pie implicitly final 


public class Java8LambdaScope {

    public static void main(String[] args) {
        double pie=3.14; // Implicitly final variable
        LambdaScope lambdaScope=(x)->pie*x*x;
        double area=lambdaScope.areaOfCircle(5);

        System.out.println(area);

    }
}

Changing the value of pie within the lambda is prohibited because the 'pie' variable is implicitly final and we can't change the value of  final variable. We will get error like // variable pie is accessed from within inner class, need to be final or effectively final.


public static void main(String[] args) {
        double pie=3.14;
        LambdaScope lambdaScope1=new LambdaScope() {
            @Override
            public double areaOfCircle(int radious) {
// Error here
                pie=2.4// variable pie is accessed from within inner class, need to be final or effectively final.
                return pie*radious*radious;
            }
        };

}


Accessing fields and static variables: In constant to local variables we have both read and write access to instance fields and static variables from within lambda expressions. This behavior is well known from anonymous objects.


public class Java8LambdaScope {
   double pie=3.14;
   static double pie1=3.14;

    public static void main(String[] args) {

        Java8LambdaScope lambdaScope1=new Java8LambdaScope();
        lambdaScope1.testScope();

    }

    private  void testScope() {
        LambdaScope lambdaScope1=new LambdaScope() {
            @Override
            public double areaOfCircle(int radious) {
                pie=2.4;
                return pie*radious*radious;
            }
        };

        LambdaScope lambdaScope2=new LambdaScope() {
            @Override
            public double areaOfCircle(int radious) {
                pie1=2.4;
                return pie*radious*radious;
            }
        };

    }
}


Accessing Default Interface Methods: In first section we have shown how to access the default interface method.

public class Java8Default {
    public static void main(String[] args) {
        Java8DefaultI java8Default = new Java8DefaultI() {
            @Override
            public void hello() {
                System.out.println("Abstract method Hello Calling !!");
            }
        };
        java8Default.hello();
        java8Default.helloWorld(); // default method access here
    }
}

Note:  Default methods cannot be accessed from within lambda expressions.

6. Method and Constructor References

Java 8 introducing new feature called method reference. Method reference is used to refer method of functional interface. It is compact and easy form of lambda expression. Each time when you are using lambda expression to just referring a method, you can replace your lambda expression with method reference. In this tutorial, we are explaining method reference concept in detail.

Types of Method References

There are following types of method references in java:

1. Reference to a static method:


public class Java8MethodStaticRef {
    public static void main(String[] args) {
// Static method reference
      Drawing drawing=Java8MethodStaticRef::drowingCircle;
      drawing.draw();

    }
    public static void drowingCircle(){
        System.out.println("Cricle Drawing");
    }
}

interface  Drawing{
     void draw();
}

    2. Reference to an instance method.

public class Java8MethodConsRef {
    public static void main(String[] args) {
// Create reference here
        ToUpperCase toUpperCase = new ToUpperCase();
        Converter<StringStringconverter = toUpperCase::upperCase;
        String converted = converter.convert("Java");
        System.out.println(converted);    // "JAVA"

    }
}

class ToUpperCase {
    String upperCase(String s) {
        return s.toUpperCase();
    }
}

class ToLowerCase {
    String lowerCase(String s) {
        return s.toLowerCase();
    }
}
3. Reference to a constructor.
See :: keyword works for constructors. Create new Employee class and define constructor.

class  Employee{
    String firstName;
    String lastName;
    Employee(){}

    Employee(String firstName,String  lastName){
        this.firstName=firstName;
        this.lastName=lastName;
    }
}

Now create the Employee factory to create the employee.

interface EmployeeFactory<P extends Employee>{
    P create(String firstName,String  lastName);
}

Without implement the EmployeeFactory method we can use this using constructor reference. 


public class Java8MethodConsRef {
    public static void main(String[] args) {
    EmployeeFactory<EmployeeemployeeFactory=Employee::new;
    employeeFactory.create("Ram","Singh");
    }

}


7: Built-in Functional Interfaces

The JDK 1.8 API contains many built-in functional interfaces. Those existing interfaces are extended to enable Lambda support via the @FunctionalInterface annotation.

Predicates

Predicates are boolean valued functions of one argument and its return true and false. The interface contains various default methods for composing predicates to complex logical terms (and, or, negate)

We'll combine Predicates using the methods Predicate.and()Predicate.or(), and Predicate.negate().




import java.util.Objects;
import java.util.function.Predicate;

public class Java8Predicates {
    public static void main(String[] args) {
        Predicate<Stringpredicate1 = (s) -> s.length() > 0;
        Predicate<Stringpredicate2 = (s) -> s.length() < 5;

        String str="Ram";
        if(predicate1.and(predicate2).test(str))
        {
            System.out.println("Valid value");
        }// true
        else {
            System.out.println("Value should be greater the 0 and less then 5");
        }
        predicate1.negate().test("shyam");

        Predicate<BooleannonNull = Objects::nonNull;
        Predicate<BooleanisNull = Objects::isNull;

        System.out.println(nonNull.test(null));

        Predicate<StringisEmpty = String::isEmpty;
        Predicate<StringisNotEmpty = isEmpty.negate();
    }
}

Functions

Functions accept one argument and produce a result. Default methods can be used to chain multiple functions together (compose, andThen).

In below example we are concatenation of two function using andThen().  Here, x function function gets executed and then y function gets executed.

import java.util.function.Function;

public class Java8Function1 {
    public static void main(String[] args) {
        Function<StringStringx = myString -> myString + ",\t Indian ";
        Function<StringStringy = myString -> myString + ",\t Man ";

        x = x.andThen(y);

        System.out.println(x.apply("Hello"));
    }
}

// Output
Hello, Indian , Man

In below example we are concatenation of two function using compose method. Here, y function composes x function first and then executes itself.

import java.util.function.Function;

public class Java8Function1 {
    public static void main(String[] args) {
        Function<StringStringx = myString -> myString + ",\t Indian ";
        Function<StringStringy = myString -> myString + ",\t Man ";

        x = x.compose(y);

        System.out.println(x.apply("Hello"));
    }
}

// Output
Hello, Indian , Man

Thus, although outcome is same, the sequence of execution is different.

Suppliers

Suppliers produce a result of a given generic type. Unlike Functions, Suppliers don’t accept arguments.


import java.time.LocalDateTime;
import java.util.function.Supplier;

public class Java8Supplier {

    public static void main(String[] args) {
        Supplier<EmployeeemployeeSupplier = Employee::new;
        employeeSupplier.get(); // Create new employee

        Supplier<LocalDateTimes = () -> LocalDateTime.now();
        LocalDateTime time = s.get();

        System.out.println(time);

    }
}

Consumers

Consumers represents operations to be performed on a single input argument.

import java.util.function.Consumer;

public class Java8Consumer {
    public static void main(String[] args) {
        Consumer<Employeegreeter = (p) -> System.out.println("Hello, " + p.firstName);
        greeter.accept(new Employee("Ram""Singh"));
    }
}

Comparators

Comparators are well known from older versions of Java. Java 8 adds various default methods to the interface.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Java8Comparator {
    public static void main(String[] args) {
      List<EmployeeemployeeList=new ArrayList<>();
      ini(employeeList);

      employeeList.sort(Comparator.comparing(Employee::getFirstName).
            thenComparing(Employee::getLastName));
      System.out.println(employeeList.toString());

    }

    private static void ini(List<EmployeeemployeeList) {
        employeeList.add(new Employee("Ram","Singh"));
        employeeList.add(new Employee("Shyam","Singh"));
        employeeList.add(new Employee("Jeevan","Ram"));
        employeeList.add(new Employee("Radhey","Shyam"));
        employeeList.add(new Employee("Ram","Pyare"));
    }

}

Optionals:  A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Instead of returning null you return an Optional in Java 8.

import java.util.Optional;

public class Java8Optional {
    public static void main(String[] args) {
        String str="ram";
        Optional<Stringoptional = Optional.of(str);

        optional.isPresent();
        optional.get();

        optional.ifPresent((s) -> System.out.println(s.charAt(0)));
    }
}


No comments:

Post a Comment