Quick Refresh : Java 8 : Features
Quick Refresh: Java 8: features
Some of the important Java 8 features are:
- forEach() method in Iterable interface
- default and static methods in Interfaces
- Functional Interfaces and Lambda Expressions
- Java Stream API for Bulk Data Operations on Collections
- Java Time API
- Collection API improvements
- Concurrency API improvements
- Java IO improvements
- Miscellaneous Core API improvements
Java8 interface changes include static methods and default methods in interfaces. Prior to Java 8, we could have only method declarations in the interfaces. But from Java 8, we can have default methods and static methods in the interfaces. We can use
default
and static
keyword to create interfaces with method implementation.
If you read forEach method details carefully, you will notice that it’s defined in the Iterable interface but we know that interfaces can’t have a method body. From Java 8, interfaces are enhanced to have a method with implementation. forEach method implementation in the Iterable interface is:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action) ;
for (T t : this) {
action.accept(t);
}
}
We know that Java doesn’t provide multiple inheritance in Classes because it leads to Diamond Problem. So how it will be handled with interfaces now since interfaces are now similar to abstract classes. The solution is that the compiler will throw an exception in this scenario and we will have to provide implementation logic in the class implementing the interfaces.
package com.java8. defaultmethod;
@FunctionalInterface
public interface Interface1 {
void method1(String str);
default void log(String str){
System.out.println("I1 logging::"+str);
}
static void print(String str){
System.out.println("Printing "+str);
}
//trying to override Object method gives compile time error as
//"A default method cannot override a method from java.lang.Object"
// default String toString(){
// return "i1";
// }
}
package com.java8. defaultmethod;
@FunctionalInterface
public interface Interface2 {
void method2();
default void log(String str){
System.out.println("I2 logging::"+str);
}
}
Notice that both the interfaces have a common method log() with implementation logic.
package com.java8. defaultmethod;
public class MyClass implements Interface1, Interface2 {
@Override
public void method2() {
}
@Override
public void method1(String str) {
}
//MyClass won't compile without having it's own log() implementation
@Override
public void log(String str){
System.out.println("MyClass logging::"+str);
Interface1.print("abc");
}
}Java 8 uses default and static method s heavily in and default methods are added so that our code remains backward compatible.
In Java, If any class in the hierarchy has a method with the same signature, then default methods become irrelevant. Since any class implementing an interface already has Object as a superclass, if we have equals(), hashCode() default methods in the interface, it will become irrelevant. That's why for better clarity, interfaces are not allowed to have Object class-default methods.
Functional interface in Java 8:
As the name suggests, a functional interface is an interface that represents a function. Technically, an interface with just one abstract method is called a functional interface.
You can also use @FunctionalInterface to annotated a functional interface. In that case, the compiler will verify if the interface actually contains just one abstract method or not. It's like the @Override annotation which prevents you from accidental errors.
Another useful thing to know is that If a method accepts a functional interface then you can pass a lambda expression to it. Some examples of the functional interface are Runnable, Callable, Comparator, and Comparable from old API and Supplier, Consumer, and Predicate, etc from new function API.
As the name suggests, a functional interface is an interface that represents a function. Technically, an interface with just one abstract method is called a functional interface.
You can also use @FunctionalInterface to annotated a functional interface. In that case, the compiler will verify if the interface actually contains just one abstract method or not. It's like the @Override annotation which prevents you from accidental errors.
Another useful thing to know is that If a method accepts a functional interface then you can pass a lambda expression to it. Some examples of the functional interface are Runnable, Callable, Comparator, and Comparable from old API and Supplier, Consumer, and Predicate, etc from new function API.
Lambda Expressions: In very simple terms, a lambda expression is a function that can be referenced and passed around as an object. Lambda expressions introduce functional style processing in Java and facilitate the writing of compact and easy-to-read code. Because of this, lambda expressions are a natural replacement for anonymous classes as method arguments. One of their main uses is to define inline implementations of functional interfaces. A lambda expression consists of two parts: the parameter part and the expressions part separated by a forward arrow as below:
params -> expressions
Any lambda expression has the following characteristics:
Optional type declaration – when declaring the parameters on the left-hand side of the lambda, we don’t need to declare their types as the compiler can infer them from their values. So int param -> … and param ->… are all valid
Optional parentheses – when only a single parameter is declared, we don’t need to place it in parentheses. This means param -> … and (param) -> … are all valid. But when more than one parameter is declared, parentheses are required
Optional curly braces – when the expressions part only has a single statement, there is no need for curly braces. This means that param – > statement and param – > {statement;} are all valid. But curly braces are required when there is more than one statement
Optional return statement – when the expression returns a value and it is wrapped inside curly braces, then we don’t need a return statement. That means (a, b) – > {return a+b;} and (a, b) – > {a+b;} are both valid
params -> expressions
Any lambda expression has the following characteristics:
Optional type declaration – when declaring the parameters on the left-hand side of the lambda, we don’t need to declare their types as the compiler can infer them from their values. So int param -> … and param ->… are all valid
Optional parentheses – when only a single parameter is declared, we don’t need to place it in parentheses. This means param -> … and (param) -> … are all valid. But when more than one parameter is declared, parentheses are required
Optional curly braces – when the expressions part only has a single statement, there is no need for curly braces. This means that param – > statement and param – > {statement;} are all valid. But curly braces are required when there is more than one statement
Optional return statement – when the expression returns a value and it is wrapped inside curly braces, then we don’t need a return statement. That means (a, b) – > {return a+b;} and (a, b) – > {a+b;} are both valid
Java 8 Method Reference: A method reference is the shorthand syntax for a lambda expression that executes just ONE method. Here's the general syntax of a method reference:
Object :: methodName
We know that we can use lambda expressions instead of using an anonymous class. But sometimes, the lambda expression is really just a call to some method, for example:
Consumer<String> c = s -> System.out.println(s);
To make the code clearer, you can turn that lambda expression into a method reference:
Consumer<String> c = System.out::println;
Consumer<String> c = s -> System.out.println(s);
To make the code clearer, you can turn that lambda expression into a method reference:
Consumer<String> c = System.out::println;
a method reference can't be used for any method. They can only be used to replace a single-method lambda expression. So to use a method reference, you first need a lambda expression with one method. And to use a lambda expression, you first need a functional interface, an interface with just one abstract method.
In other words:
In other words:
Instead of using AN ANONYMOUS CLASS you can use A LAMBDA EXPRESSION And if this just calls one method, you can use A METHOD REFERENCE
There are four types of method references:
(1) A method reference to a static method:
(1) A method reference to a static method:
In this case, we have a lambda expression like the one below:
(args) -> Class.staticMethod(args)
This can be turned into the following method reference:
Class::staticMethod
(args) -> Class.staticMethod(args)
This can be turned into the following method reference:
Class::staticMethod
For example:
class Numbers {
public static boolean isMoreThanFifty(int n1, int n2) {
return (n1 + n2) > 50;
}
public static List<Integer> findNumbers(
List<Integer> l, BiPredicate<Integer, Integer> p) {
List<Integer> newList = new ArrayList<>();
for(Integer i : l) {
if(p.test(i, i + 10)) {
newList.add(i);
}
}
return newList;
}
}
We can call the findNumbers() method:
List<Integer> list = Arrays.asList(12,5,45,18,33, 24,40);
// Using an anonymous class
findNumbers(list, new BiPredicate<Integer, Integer>() {
public boolean test(Integer i1, Integer i2) {
return Numbers.isMoreThanFifty(i1, i2);
}
});
// Using a lambda expression
findNumbers(list, (i1, i2) -> Numbers.isMoreThanFifty(i1, i2));
// Using a method reference
findNumbers(list, Numbers::isMoreThanFifty);
class Numbers {
public static boolean isMoreThanFifty(int n1, int n2) {
return (n1 + n2) > 50;
}
public static List<Integer> findNumbers(
List<Integer> l, BiPredicate<Integer, Integer> p) {
List<Integer> newList = new ArrayList<>();
for(Integer i : l) {
if(p.test(i, i + 10)) {
newList.add(i);
}
}
return newList;
}
}
We can call the findNumbers() method:
List<Integer> list = Arrays.asList(12,5,45,18,33,
// Using an anonymous class
findNumbers(list, new BiPredicate<Integer, Integer>() {
public boolean test(Integer i1, Integer i2) {
return Numbers.isMoreThanFifty(i1, i2);
}
});
// Using a lambda expression
findNumbers(list, (i1, i2) -> Numbers.isMoreThanFifty(i1, i2));
// Using a method reference
findNumbers(list, Numbers::isMoreThanFifty);
(2) A method reference to an instance method of an object of a particular type:
In this case, we have a lambda expression like the following:
(obj, args) -> obj.instanceMethod(args)
Where an instance of an object is passed, and one of its methods is executed with some optional(s) parameter(s).
This can be turned into the following method reference:
ObjectType::instanceMethod
(obj, args) -> obj.instanceMethod(args)
Where an instance of an object is passed, and one of its methods is executed with some optional(s) parameter(s).
This can be turned into the following method reference:
ObjectType::instanceMethod
For Example:
class Shipment {
public double calculateWeight() {
double weight = 0;
// Calculate weight
return weight;
}
}
And this method:
public List<Double> calculateOnShipments(
List<Shipment> l, Function<Shipment, Double> f) {
List<Double> results = new ArrayList<>();
for(Shipment s : l) {
results.add(f.apply(s));
}
return results;
}
public double calculateWeight() {
double weight = 0;
// Calculate weight
return weight;
}
}
And this method:
public List<Double> calculateOnShipments(
List<Shipment> l, Function<Shipment, Double> f) {
List<Double> results = new ArrayList<>();
for(Shipment s : l) {
results.add(f.apply(s));
}
return results;
}
We can call that method using:
List<Shipment> l = new ArrayList<Shipment>();
// Using an anonymous class
calculateOnShipments(l, new Function<Shipment, Double>() {
public Double apply(Shipment s) { // The object
return s.calculateWeight(); // The method
}
});
// Using a lambda expression
calculateOnShipments(l, s -> s.calculateWeight());
// Using a method reference
calculateOnShipments(l, Shipment::calculateWeight);
List<Shipment> l = new ArrayList<Shipment>();
// Using an anonymous class
calculateOnShipments(l, new Function<Shipment, Double>() {
public Double apply(Shipment s) { // The object
return s.calculateWeight(); // The method
}
});
// Using a lambda expression
calculateOnShipments(l, s -> s.calculateWeight());
// Using a method reference
calculateOnShipments(l, Shipment::calculateWeight);
(3) A method reference to an instance method of an existing object.
In this case, we have a lambda expression like the following:
(args) -> obj.instanceMethod(args)
This can be turned into the following method reference:
obj::instanceMethod
(args) -> obj.instanceMethod(args)
This can be turned into the following method reference:
obj::instanceMethod
(4) A method reference to a constructor: The only thing this lambda expression does is to create a new object and we just reference a constructor of the class with the keyword
new
In this case, we have a lambda expression like the following:
(args) -> new ClassName(args)
That can be turned into the following method reference:
ClassName::new
(args) -> new ClassName(args)
That can be turned into the following method reference:
ClassName::new
Comments
Post a Comment