Table of Contents
Java 8 Features Interview Questions
1. What New Features Were Added in Java 8?
Java 8 ships with several new features but the most significant are the following:
Lambda Expressions − a new language feature allowing treating actions as objects
Method References − enable defining Lambda Expressions by referring to methods directly using their names
Optional − special wrapper class used for expressing optionality
Stream API − a special iterator class that allows processing collections of objects in a functional manner
Functional Interface – an interface with maximum one abstract method, implementation can be provided using a Lambda Expression
Default methods − give us the ability to add full implementations in interfaces besides abstract methods
Nashorn, JavaScript Engine − Java-based engine for executing and evaluating JavaScript code
2. How Streams differ from collections?
No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, “find the first String with three consecutive vowels” need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.
In contrast, a Collection is a container of objects (elements). You can’t get (retrieve) an object from a collection unless the object was previously added to the collection.
3. What is the use of Functional Interface in Java 8?When to use Functional Interface in Java 8?
Reference – PART 1 : FUNCTIONAL INTERFACE AND DEFAULT METHODS IN JAVA 8
An interface having only single abstract method is called as functional interface. Functional interface can have multiple default or static methods.
We can create a functional interface as below :
package com.onlyfullstack.lambdaAndFunctional;
public interface IConnection {
public void connect();
default void print() {
System.out.println("in default method");
}
static void description() {
System.out.println("in static method");
}
}
Functional interface can have only one abstract method(method without body) and can have many default and static methods. As of now we don’t have any restriction to add more abstract methods in above interface but it will break the functional interface rule.
This is an Interface level annotation type used on functional interface to restrict the user from adding more than one abstract method.
If we try to add two or more abstract methods in @FunctionalInterface then it will give us a compilation error as below :
Invalid ‘@FunctionalInterface’ annotation; IConnection is not a functional interface
4. What is the need of static method in Interface ?
Java 8 has added static methods in interface to provide utility methods on interface level without creating the object of the interface.
Some of the static methods of the java 8 are as below :
Stream.of()
Stream.iterate()
Stream.empty()
5. What is the need of having a default method in an interface ?
We can provide some default functionality with default methods.
The original motivation to introduce default methods to Java 8 was the desire to extend the Collections Framework interfaces with lambda-oriented methods without breaking any existing implementations. Although this is more relevant to the authors of public libraries.
Java has to implement forEach functionality on Collections but we cant not add a new abstract method in each interface and add the implementation of them into their respective implementation classes. This approach will break the existing code and force everyone to give the implementation of that new method.
What if I say that the implementation of forEach method is common for all List ? Still do we need to define the same method in all classes(ArrayList or LinkedList) ?
The answer is No. For example The forEach isn’t declared by java.util.List nor the java.util.Collection interface yet. One obvious solution would be to just add the new method to the existing interface and provide the implementation where required in the JDK. However, once published, it is impossible to add methods to an interface without breaking the existing implementation.
The benefit that default methods bring is that now it’s possible to add a new default method to the interface and it doesn’t break the old implementations. So that’s how java has added lots of default methods without breaking the implementation and provided the backward compatibility.
Some of the default methods available in Map interface :
default V putIfAbsent(K key, V value)
default boolean remove(Object key, Object value)
Reference – PART 1 : FUNCTIONAL INTERFACE AND DEFAULT METHODS IN JAVA 8
6. What is Lambda Expression in Java 8?
Lambda expression facilitates the functional programming and simplifies the development. Lambda expression is a shorter way of writing an implementation of a single abstract method interface. Lambda expressions are used to create anonymous functions.
Syntax:
(parameters) -> expression
or
(parameters) -> { statements; }
Java 8 provide support for lambda expressions only with functional interfaces.
Lets see how to use lambda to remove the boilerplate code. We will implement the Runnable interface with anonymous class and with lambda expression.
private static void simpleConceptWithRunnable() { Runnable runnable = new Runnable() { // boilerplate code which needs to be written everytime @Override public void run() { System.out.println("Inside annonymos inner class"); } }; Runnable runnable2 = () -> { // Lambda expression System.out.println("Inside lambda expression"); }; Thread thread = new Thread(runnable); thread.start(); thread = new Thread(runnable2); thread.start(); }
We wrote a boilerplate code of creating an anonymous class and override the run method. Lambda takes care of this things and gives us a simple syntax to create a anonymous class. Lambdas are more than anonymous classes.
Reference – PART 2 – LAMBDA EXPRESSION IN JAVA 8
7. What is Method Reference in Java 8?
Method reference is a short form of lambda expressions used on Functional Interface. We can replace our lambda expression with Method reference to clean the code.
Reference – PART 3 – METHOD REFERENCE IN JAVA 8
8. What are different types of Method References in Java 8?
1. Reference to Static Method
Suppose that we have a static method to print the value as printMe. Now we have a stream some integers and we want to print even numbers in forEach block, Here we have used Lambda expression and passed the current value to printMe method as below :
private static void staticReference_WithMethodRef() { Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9); stream.filter(i -> i%2==0 ) .forEach(MethodReference::printMe); }
2. Reference to Instance Method from Class type
We apply the Method Reference on the instance method with class name. We are using student.getName() to get the name of the student with Lambda expression. Map method will map the Student object to String object with getName() method.
private static void instanceReferenceWithClass_WithMethodRef() { List<Student> students = JavaInputFixture.createList(); Set<String> names = students.stream() .map(Student::getName) // this will convert the Student Stream into String Stream by // applying the getName() .collect(Collectors.toSet()); System.out.printf("Names of all students %sn", names); }
Here
Student::getName is equivalent to
.map(student -> student.getName()) where student is an object of Student class
3. Reference to Instance Method from instance
private static void instanceReference_WithMethodRef() { Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9); stream.filter(i -> i%2==0 ) .forEach(System.out::println); }
Reference – PART 3 – METHOD REFERENCE IN JAVA 8
9. Why to use Optional in Java 8?
Suppose we have a class structure as below and want to get the dedicated memory of the Graphics Card, but all smartphones may not have the graphics card so there are chances of Graphics Card or Dedicated Memory objects as null.
Our class structure looks like :
Let’s go with the traditional java approach :
private static void traditionalApproach(SmartPhone smartPhone) {
String size = "unknown";
if(smartPhone.getGraphicsCard()!=null) {
GraphicsCard graphicsCard = smartPhone.getGraphicsCard().get();
if(graphicsCard != null) {
GraphicsMemory graphicsMemory = graphicsCard.getGraphicsMemory();
if(graphicsMemory != null && graphicsMemory.getDedicatedMemory() != null) {
size = graphicsMemory.getDedicatedMemory();
}
}
}
System.out.println("Size : " + size + ", for Object : "+smartPhone.toString());
}
So how do we achieve this with less code and tell java to take care of handling the null pointers for us?
Reference – PART 4 – OPTIONAL IN JAVA 8
10. What is Optional in Java 8?
Java 8 introduced a new class java.util.Optional<T> which encapsulates a value. 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.
Additional methods that depend on the presence or absence of a contained value are provided, such as orElse(). The Optional may contain an Object or an empty instance of a class or a null value.
For more methods of Optional please refer to below url – https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
With Optional :
private static void getGraphicsCardSize(Lis) {
String size = smartPhone.getGraphicsCard()
.map(GraphicsCard::getGraphicsMemory) // map returns the Optional of type passed as a parameter (here its string)
.map(GraphicsMemory::getDedicatedMemory)
.orElse("unkonwn");
System.out.println("Size : " + size + ", for Object : "+smartPhone.toString());
}
So this is how optional simplifies our life.
Reference – PART 4 – OPTIONAL IN JAVA 8
11. What are different ways to create Optional?
1. Optional.empty() – This method will return an empty Optional object.
Optional<GraphicsCard> card = Optional.empty();
2. Optional.of() – This method will return an Optional of object passed as an argument to the of method. Returns an Optional with the specified present non-null value.
Optional<GraphicsCard> card = Optional.ofNullable(new GraphicsCard());
3. Optional. ofNullable() – Returns an Optional describing the specified value, if non-null,
otherwise returns an empty Optional
Optional<GraphicsCard> card = Optional.ofNullable(null);
Reference – PART 4 – OPTIONAL IN JAVA 8
12. What is the Difference in between Optional.of() and Optional.ofNullable() ?Optional.of() vs Optional.ofNullable() ?
public static <T> Optional<T> of(T value) { return new Optional<>(value); }
Optional.ofNullable() will return the object of Optional with a null check. If this method gets the null as an input then it will return the empty Optional otherwise it returns an Optional with the specified present non-null value
Let’s look at the interns of Optional.ofNullable() method:
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
Optional.of() should be used when you are sure that Optional will never have a null object and it will contain the object value or it will be empty but it will not be null. Optional.of() can also throw a NullPointerEception if the Optional is created with a null value.
Optional.ofNullable()- is used when there are chances that the object might be null.
Reference – PART 4 – OPTIONAL IN JAVA 8
13. What are methods available in Optional?
1. isPresent() – This method returns true if the object is present into an Optional object.
Optional<GraphicsCard> graphicsCard = Optional.of(new GraphicsCard()); if(graphicsCard.isPresent()) { System.out.println(graphicsCard.get()); }
2. get() – If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException.
GraphicsCard newCard = graphicsCard.get();
3. ifPresent(Consumer<? super T> consumer) – If a value is present, invoke the specified consumer with the value, otherwise do nothing.
Optional<GraphicsCard> graphicsCard = Optional.of(new GraphicsCard()); graphicsCard.ifPresent(System.out::println);
4. orElse(T other) – Return the value if present, otherwise returns the object passed as a parameter. Internals of orElse(0 method in Java 8 Optional class.
public T orElse(T other) { return value != null ? value : other; }
GraphicsCard newCard = graphicsCard.orElse(new GraphicsCard());
5. orElseThrow(Supplier<? extends X> exceptionSupplier) – Return the contained value, if present, otherwise throw an exception to be created by the provided supplier.
GraphicsCard newCard = graphicsCard.orElseThrow(IllegalArgumentException::new);
We will see the rest of the methods(map, flatMap, filter) in our upcoming tutorials.
Reference – PART 4 – OPTIONAL IN JAVA 8
14. How to filter the list of Student object who are from Pune?
filter allows you to filter the stream to process the stream.
Syntax : Stream<T> filter(Predicate<? super T> predicate)
findAny(0 will return the filtered object from the stream.
Lets see this with some examples :
Create a list of Student :
private static List<Student> createList() { Student student1 = new Student("Saurabh", "Pune", 26); Student student2 = new Student("Robert", "Pune", 25); Student student3 = new Student("John", "Mumbai", 21); Student student4 = new Student("Roman", "Pune", 18); Student student5 = new Student("Randy", "Mumbai", 17); return Arrays.asList(new Student[] {student1, student2, student3, student4, student5}); } private static void filter(List<Student> students) { System.out.println("Executing filter() :"); // Print all the students who lives in Pune System.out.println("*** Students who lives in Pune *** "); students.stream() .filter(student -> "Pune".equals(student.getCity())) .forEach(System.out::println); // Find the student whose name is Saurabh and return null if not found System.out.println("*** Students whos name is Saurabh *** "); Student stud = students.stream() .filter(student -> SAURABH.equals(student.getName())) .findAny() .orElse(null); // it will return null if filter does find any object matching the criteria System.out.println(stud); System.out.println("Ending the execution of filter()"); }
Output :
Executing filter()
*** Students who lives in Pune ***
Student [name=Saurabh, city=Pune, age=26]
Student [name=Robert, city=Pune, age=25]
Student [name=Roman, city=Pune, age=18]
15. How to find the object meeting certain criteria?
we can use findAny() / findFirst() on stream to find the object based on the criteria passed an an input.
Here we are finding the Student whose name is Saurabh.
private static List<Student> createList() { Student student1 = new Student("Saurabh", "Pune", 26); Student student2 = new Student("Robert", "Pune", 25); Student student3 = new Student("John", "Mumbai", 21); Student student4 = new Student("Roman", "Pune", 18); Student student5 = new Student("Randy", "Mumbai", 17); return Arrays.asList(new Student[] {student1, student2, student3, student4, student5}); } private static void filter(List<Student> students) { System.out.println("Executing filter() :"); // Find the student whose name is Saurabh and return null if not found System.out.println("*** Students whos name is Saurabh *** "); Student stud = students.stream() .filter(student -> SAURABH.equals(student.getName())) .findAny() .orElse(null); // it will return null if filter does find any object matching the criteria System.out.println(stud); System.out.println("Ending the execution of filter()"); }
Student [name=Saurabh, city=Pune, age=26]
Ending the execution of filter()
16. How to use map method?
What is the use of map method?
Write a program to get the age of Student whose name is Saurabh
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
a stream consisting of the results of applying the given function to the elements of this stream. Map takes an input which describes how the value needs to be transformed into.
Suppose we want to get the age of the Student whose name is Saurabh, till now we have only retrieved the complete object from the stream but how do we do this ?
We can use map() to transform the Student Stream into the age stream as below.
int age = students.stream() .filter(student -> SAURABH.equals(student.getName())) .map(Student::getAge) .findAny() .orElse(0); System.out.printf("*** Age of %s is %dn",SAURABH, age);
Reference – PART 6 – MAP() VS FLATMAP() IN JAVA 8
17. What is the use of flatMap() in Java 8?What is the difference between map() and flatMap()?
Set<String> courses = students.stream() .map(Student::getCourses) .collect(Collectors.toSet());
Here we will get a compilation error as below
Type mismatch: cannot convert from Set<String[]> to Set<String>
To resolve this issue we use flatMap()
flatMap()
It returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
flatMap will transform the stream of stream into simple stream. In below example we are using the flatMap to convert the Array of Stream into the String stream.
Set<String> courses = students.stream() .map(Student::getCourses) .flatMap(Arrays::stream) .collect(Collectors.toSet());
18. What are short circuiting operations in Java 8?
If(str != null && str.length>10)
In this condition, if we pass str as null then it will only execute the first condition and evaluate the result. The same concept of short circuiting java 8 uses in Stream.
Reference – PART 8 – SHORT CIRCUITING OPERATIONS OF STREAMS IN JAVA 8
19. What are different short circuiting operations in Java 8?
1. limit() in Java 8
int [] arr = {1,2,3,4,5,6,7,8,9,11}; System.out.println("*** Printing the first 5 elements of stream :"); Arrays.stream(arr) .limit(5) .forEach(System.out::println);
Output :
*** Printing the first 5 elements of stream :
1
2
3
4
5
2. findFirst(), findAny() in Java 8
int [] arr = {1,2,3,4,5,6,7,8,9}; Integer findFirst = Arrays.stream(arr).filter(i -> (i%2) == 0) .findFirst() .orElse(-1); System.out.println("findFirst : " + findFirst); Integer findAny = Arrays.stream(arr).filter(i -> (i%2) == 0) .findAny() .orElse(-1); System.out.println("findAny : " + findAny);
Output :
findFirst : 2
findAny : 6
3. allMatch() anyMatch() noneMatch() in Java 8
anyMatch() – this will evaluate the condition in the stream until it finds its match and once it finds the match, It will exit the processing and retruns the rboolean value.
noneMatch() – This is exact opposite of allMatch()
Lets see their behavior through some practical example.
int [] arr = {1,2,3,4,5,6,7,8,9,11}; System.out.println("All numbers are less than 12 : " + Arrays.stream(arr).allMatch(i-> i < 12)); System.out.println("Contains any numbers greater than 10 : " + Arrays.stream(arr).anyMatch(i-> i == 8)); System.out.println("All numbers are less than 10 : " + Arrays.stream(arr).noneMatch(i-> i > 10));
20. What is Intermediate and Terminal Operations of Stream in Java 8?
Stream
We can create stream from object with the help of Stream.of() method.
Intermediate Operations in Java 8
Intermediate operation will transform a stream into another stream, they are composed forming a Pipeline of Stream execution and will not be able to execute until some terminal operation is invoked. Intermediate Operations are lazy, so they don’t get executed until the actual processing is required. Every Intermediate Operation will return the new Stream and traversal of each Stream does not begin until the terminal operation of the pipeline is executed.
Lets see how intermediate operations are lazy ?
We have a map() function in which we are printing the current student name. These names will only be printed if we apply a terminal operator to it. In below example we have applied the collect(terminal operator) and the map() prints the student names after the thread comes into running state. This is how intermediate operations works.
private static void lazyIntermediateOperations(List<Student> students) throws InterruptedException { System.out.println("######## Executing lazyIntermediateOperations() : ######## "); Stream<String> studentStream = students.stream() .map(student -> { System.out.printf("In Map : %sn", student.getName()); return student.getName().toUpperCase(); }); System.out.println("After map statement"); Thread.sleep(5000); System.out.println("Thread is in Running state now"); studentStream.collect(Collectors.toList()); System.out.println("######## Ending the execution of lazyIntermediateOperations() ######## "); }
Output
######## Executing lazyIntermediateOperations() : ########
After map statement
Thread is in Running state now
In Map : Saurabh
In Map : Robert
In Map : John
In Map : Roman
In Map : Randy
######## Ending the execution of lazyIntermediateOperations() ########
Intermediate Operations available in Java 8 :
• filter(Predicate<T>)
• map(Function<T>)
• flatmap(Function<T>)
• sorted(Comparator<T>)
• peek(Consumer<T>)
• distinct()
• limit(long n)
• skip(long n)
Terminal Operations in Java 8
Terminal operations produces a non-stream (cannot be chained) result such as primitive value, a collection or no value at all.
Terminal Operations available in Java 8 :
forEach
toArray
reduce
collect
min
max
count
Short Circuiting Terminal Operations available in Java 8 :
anyMatch
allMatch
noneMatch
findFirst
findAny
Last 5 are short-circuiting terminal operations.
Reference – PART 10 – INTERMEDIATE AND TERMINAL OPERATIONS OF STREAM IN JAVA 8
21. Difference between Intermediate and Terminal Operations in Java 8Intermediate vs Terminal Operations in Java 8
Sr. No.
|
Intermediate Operations
|
Terminal Operations
|
1
|
They produce stream
|
They produce result such as primitive value, object or collection
|
2
|
They are lazy and they only gets executed when any terminal operation is performed.
|
They are executes immediately and depends on intermediate
operations to get the result.
|
3
|
We can chain multiple Intermediate Operations like
.filter(some condition)
.map(some function)
.flatMap(some function)
|
We cannot chain multiple terminal operations and each stream
can have only single terminal operation at a time.
|
Very nice blog sir ji
Really helpful 🙂