Question1 ➽ what are new features added in java8?
Interface Default Method
Interface static methods
Functional Interfaces
Java 8 has defined a lot of
functional interfaces in
4.Method and Constructor References
Java 8 has been
released in March 2014, so it’s one of the hot topic in java interview
questions. Java 8 has been one of the biggest release after Java 5 annotations
and generics. Some of the important features of Java 8 are:
A.
Interface changes with default and static methods
B.
Functional interfaces and Lambda Expressions
C.
Java Stream API for collection classes
D.
Java Date Time API
Interface changes
with default and static methods
One of the biggest design change in Java 8 is with the
concept of interfaces. Prior to Java 7, we could have only method declarations
in the interfaces. But from Java 8, we can have default
methods and static
methods in the
interfaces.
Designing interfaces have always been a tough job because if we want to add additional
methods in the interfaces, it will require change in all the implementing classes. As
interface grows old, the number of classes implementing it might grow to an
extent that it’s not possible to extend interfaces. That’s why when designing
an application, most of the frameworks provide a base implementation class and
then we extend it and override methods that are applicable for our application.
Let’s look into the default and static interface methods and
the reasoning of their introduction.
Interface Default Method
For creating a default method in the interface, we need to use “default” keyword with the
method signature. For example,
public interface Interface1 {
void method1(String str);
default void log(String str){
System.out.println("I1
logging::"+str);
print(str);
}
}
Notice that log(String str) is the default method in the
Interface1
. Now
when a class will implement Interface1, it is not mandatory to provide
implementation for default methods. This feature will help us in extending
interfaces with additional methods, all we need is to provide a default
implementation.
Let’s say we have another interface with following methods:
package com.journaldev.java8.defaultmethod;
public interface lt {
void method2();
default void log(String str){
System.out.println("I2
logging::"+str);
}
}
We know that Java doesn’t allow us to extend multiple classes because it
will result in the “Diamond Problem” where compiler can’t decide which
superclass method to use. With the default methods, the diamond problem would
arise for interfaces too. Because if a class is implementing both
Interface1
andInterface2
and doesn’t implement the
common default method, compiler can’t decide which one to chose.
Extending multiple interfaces
are an integral part of Java, you will find it in the core java classes as
well as in most of the enterprise application and frameworks. So to make sure, this problem
won’t occur in interfaces, it’s made mandatory to provide implementation for
common default methods. So if a class is implementing both the above
interfaces, it will have to provide implementation for
log()
method otherwise compiler
will throw error.
public class MyClass
implements Interface1, Interface2 {
@Override
public void method2()
{
}
@Override
public void method1(String
str) {
}
@Override
public void log(String
str){
System.out.println("MyClass
logging::"+str);
Interface1.print("abc");
}
}
Important points about
interface default methods:
1.
Default methods will help us in extending interfaces without
having the fear of breaking implementation classes.
2. Default methods has
bridge down the
differences between interfaces and abstract classes.
3.
Default methods will help us in avoiding utility classes, such
as all the Collections class method can be provided in the interfaces itself.
4.
Default methods will help us in removing base implementation
classes, we can provide default implementation and the implementation classes
can chose which one to override.
5.
One of the major reason for introducing default methods is to enhance the Collections API in
Java 8 to support lambda expressions.
·
Lambda expression are
essentially of type of functional interface. To support lambda expressions
seamlessly, all core classes have to be modified. But these core classes like
java.util.List are implemented not only in JDK classes, but also in thousands
of client code as well. Any incompatible change in core classes will back fire
for sure and will not be accepted at all.
·
Default methods break
this deadlock and allow adding support for functional interface in core
classes.
Let’s see an example.
Below is a method which has been added to java.lang.Iterable.
default void forEach(Consumer<? super T>
action) {
Objects.requireNonNull(action);
for (T
t : this) {
action.accept(t);
}
}
Before java 8, if you had
to iterate on a java collection then your would get an iterator instance and
call it’s next method until hasNext() returns false. This is common code and
have been used thousands of time in day to day programming by us. Syntax is
also always same. So can we make it compact so that it takes only single line
of code and still do the job for us as before. Above function does that.
public class Animal implements Moveable{
public static void main(String[] args){
List<Animal> list = new ArrayList();
list.add(new Animal());
list.add(new Animal());
list.add(new Animal());
//Iterator code reduced to one line
list.forEach((Moveable p) -> p.move());
}
}
public interface Moveable {
default void move(){
System.out.println("I am moving");
}
}
6.
If any class in the hierarchy has a method with same signature, then
default methods become irrelevant. A default method cannot override a
method from java.lang.Object. The reasoning is very simple, it’s because Object is the
base class for all the java classes. So even if we have Object class
methods defined as default methods in interfaces, it will be useless because
Object class method will always be used. That’s why to avoid confusion, we can’t have default methods that are overriding Object
class methods.
7.
Default methods are also referred to as Defender Methods or
Virtual extension methods.
Interface static methods
Static methods are similar to default methods except that we
can’t override them in the implementation classes. This feature
helps us in avoiding undesired results incase of poor implementation in child
classes. Let’s look into this with a simple example.
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData
Print::" +
str);
}
static boolean isNull(String str) {
System.out.println("Interface
Null Check");
return str == null ? true :
"".equals(str) ? true :
false;
}
}
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl
Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl
obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
Note that
isNull(String str)
is a simple class method, it’s not overriding the interface
method. For example, if we will add @Override
annotation to the isNull() method, it will result in compiler
error.
Now when we will run the
application, we get following output
Interface Null Check
Impl Null Check
|
If we make the
interface method from static to default, we will get following output.
1
2
3
|
Impl Null Check
MyData Print::
Impl Null Check
|
The static methods are visible to interface methods only, if we
remove the isNull() method from theMyDataImpl class, we won’t be able to use it for
the MyDataImpl object. However like other static methods, we can use
interface static methods using class name. For example, a valid statement will
be:
1
|
boolean result
= MyData.isNull("abc");
|
Important points about
interface static methods:
1.
Interface static methods are part of interface, we can’t use it
for implementation class objects.
2.
Interface static methods are good for providing utility methods,
for example null check, collection sorting etc.
3.
Interface static method helps us in providing security by not
allowing implementation classes to override them.
4.
We can’t define static methods for Object class methods, we will
get compiler error as “This static method cannot hide the instance method from
Object”. This is because it’s not allowed in java, since Object is the base
class for all the classes and we can’t have one class level static method and
another instance method with same signature.
5.
We can use static interface methods to remove utility classes
such as Collections and move all of it’s static methods to the corresponding
interface, that would be easy to find and use.
Functional Interfaces
An
interface with exactly one abstract method is known as Functional Interface.
A new annotation
@FunctionalInterface has been introduced to mark an interface as Functional
Interface. @FunctionalInterface annotation is a facility to avoid accidental
addition of abstract methods in the functional interfaces. It’s optional but
good practice to use it.
Functional interfaces are long awaited and much sought out feature of
Java 8 because it enables us to uselambda
expressions to instantiate them. A new
package
java.util.function
with bunch of functional interfaces are added to provide target types
for lambda expressions and method references.
Java has always been an Object Oriented Programming language. What is means that everything
in java programming revolves around Objects (except some primitive types for
simplicity). We don’t have only functions in java, they are part of Class and
we need to use the class/object to invoke any function.
If we look into some other programming languages such as C++,
JavaScript; they are called functional programming language because we can write functions and use
them when required. Some of these languages support Object Oriented Programming
as well as Functional Programming.
Being object oriented
is not bad, but it brings a lot of verbosity to the program. For example, let’s
say we have to create an instance of Runnable. Usually we do it using anonymous
classes like below.
1
2
3
4
5
|
Runnable r = new Runnable(){
@Override
public void run()
{
System.out.println("My
Runnable");
}};
|
If you look at the
above code, the actual part that is of use is the code inside run() method.
Rest all of the code is because of the way java programs are structured.
Java 8 brings us the concept of Functional
Interfaces and Lambda
Expressions to
avoid writing all the useless code that we can easily avoid by making our java
compiler intelligent.
Java 8 has defined a lot of
functional interfaces in java.util.function
package, some of the useful ones are Consumer
, Supplier
, Function
and Predicate
.
Since there is only one abstract function in the functional
interfaces, there is no confusion in applying the lambda expression to the
method. Lambda Expressions syntax is (argument) -> (body). Now let’s see how we can write above
anonymous Runnable using lambda expression.
1
|
Runnable r1 = ()
-> System.out.println("My Runnable");
|
Lambda
Expressions
Lambda Expressions are the way through which we can
visualize functional programming in the java object oriented
world. Objects are the base of java programming language and we can never have
a function without an Object, that’s why Java language provide support for using
lambda expressions only with functional interfaces.
Since
there is only one abstract function in the functional interfaces, there is no
confusion in applying the lambda expression to the method. Lambda Expressions
syntax is (argument) -> (body). Now let’s see how we can write
above anonymous Runnable using lambda expression.
1
|
Runnable r1 = ()
-> System.out.println("My Runnable");
|
Let’s
try to understand what is happening in the lambda expression above.
·
Runnable
is a functional interface, that’s why we can use lambda expression to create
it’s instance.
·
Since
run() method takes no argument, our lambda expression also have no argument.
·
Just
like if-else blocks, we can avoid curly braces ({}) since we have a single
statement in the method body. For multiple statements, we would have to use
curly braces like any other methods.
Why do we need
Lambda Expressions
1. Reduced Lines of Code
One of the clear benefit of using lambda expression is that the amount of code is reduced, we have already seen that how easily we can create instance of a functional interface using lambda expression rather than using anonymous class.
One of the clear benefit of using lambda expression is that the amount of code is reduced, we have already seen that how easily we can create instance of a functional interface using lambda expression rather than using anonymous class.
2. Sequential and Parallel Execution Support
Another benefit of using lambda expression is
that we can benefit from the Stream API sequential and parallel operations
support.
To explain this, let’s take a simple example
where we need to write a method to test if a number passed is prime number or
not.
Traditionally we would write it’s code like
below. The code is not fully optimized but good for example purpose, so bear
with me on this.
/Traditional
approach
private static boolean isPrime(int number)
{
if(number
< 2) return false;
for(int i=2; i<number;
i++){
if(number
% i == 0) return false;
}
return true;
}
The problem
with above code is that it’s sequential in nature, if the number is very huge then it will take
significant amount of time. Another problem with code is that there are so many
exit points and it’s not readable. Let’s see how we can write the same method using
lambda expressions and stream API.
//Declarative
approach
private static boolean isPrime(int number)
{
return number > 1
&&
IntStream.range(2, number).noneMatch(
index
-> number % index == 0);
}
IntStream
is a sequence of
primitive int-valued elements supporting sequential and parallel aggregate
operations. This is the int primitive specialization of Stream
.
For more readability, we can also write the method like
below.
private static boolean isPrime(int number) {
IntPredicate
isDivisible = index -> number % index == 0;
return number > 1
&&
IntStream.range(2, number).noneMatch(
isDivisible);
}
3. Higher Efficiency with
Laziness
One more advantage of
using lambda expression is the lazy evaluation, for example let’s say we need
to write a method to find out the maximum odd number in the range 3 to 11 and
return square of it.
Usually we will write
code for this method like this:
private static int findSquareOfMaxOdd(List<Integer>
numbers) {
int max = 0;
for (int i : numbers) {
if (i % 2 != 0 && i > 3 && i < 11 && i > max)
{
max
= i;
}
}
return max * max;
}
Above program will always run in sequential order but we can
use Stream API to achieve this and get benefit of Laziness-seeking. Let’s see
how we can rewrite this code in functional programming way using Stream API and
lambda expressions.
public static int findSquareOfMaxOdd(List<Integer>
numbers) {
return numbers.stream()
.filter(NumberTest::isOdd)
//Predicate is functional interface and
.filter(NumberTest::isGreaterThan3)
// we are using lambdas to initialize it
.filter(NumberTest::isLessThan11)
// rather than anonymous inner classes
.max(Comparator.naturalOrder())
.map(i
-> i * i)
.get();
}
public static boolean isOdd(int i) {
return i % 2 != 0;
}
public static boolean isGreaterThan3(int i){
return i > 3;
}
public static boolean isLessThan11(int i){
return i < 11;
}
If you are surprised with the double colon (::) operator,
it’s introduced in Java 8 and used for method references.
Java Compiler takes care of mapping the arguments to the called method. It’s
short form of lambda expressions
i
-> isGreaterThan3(i)
or i ->
NumberTest.isGreaterThan3(i)
.
4.Method and Constructor References
A method reference is
used to refer to a method without invoking it; a constructor reference is
similarly used to refer to a constructor without creating a new instance of the
named class or array type.
Examples of method and
constructor references:
System::getProperty
System.out::println
"abc"::length
ArrayList::new
int[]::new
|
No comments:
Post a Comment
Note: only a member of this blog may post a comment.