100% found this document useful (1 vote)
219 views

Java 8 New Features

Java 8 introduced several new features including JavaFX, lambda expressions, method references, and streams. Lambda expressions allow for anonymous functions and functional interfaces. Method references provide a shorthand for lambda expressions. Streams allow for aggregate operations on collections and arrays. Default and static methods were also added to interfaces to allow new functionality to be added without breaking existing code.
Copyright
© © All Rights Reserved
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
219 views

Java 8 New Features

Java 8 introduced several new features including JavaFX, lambda expressions, method references, and streams. Lambda expressions allow for anonymous functions and functional interfaces. Method references provide a shorthand for lambda expressions. Streams allow for aggregate operations on collections and arrays. Default and static methods were also added to interfaces to allow new functionality to be added without breaking existing code.
Copyright
© © All Rights Reserved
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
You are on page 1/ 14

Java 8 Features

Some of the important Java 8 features are:


- JavaFX

JavaFX is a set of graphics and media packages that enables developers to design, create, test, debug,
and deploy rich client applications that operate consistently across diverse platforms.

- Lambda expressions
A lambda expression is an anonymous function. A function that doesn’t have a name and doesn’t belong
to any class.
//Syntax of lambda expression
(parameter_list) -> {function_body}
(x, y) -> x + y lambda expression takes two arguments x and y and returns the sum of these

Lambda expression only has body and parameter list.

1.No name – function is anonymous so we don’t care about the name


2. Parameter list
3. Body – This is the main part of the function.
4. No return type. The java 8 compiler is able to infer the return type by checking the code. you need not
to mention it explicitly.

Functional interface = An interface with only single abstract method (exemple: Runnable, callable,
ActionListener etc).

To use lambda expression, you need to either create your own functional interface or use the pre-
defined functional interface provided by Java

To use function interface:


Pre Java 8: We create anonymous inner classes.
Post Java 8: You can use lambda expression instead of anonymous inner classes.

Pre Java 8:
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("Hello World!");
}
});
Post Java 8:
b.addActionListener(e -> System.out.println("Hello World!"));
Exemplu 1: Java Lambda Expression with no parameter
@FunctionalInterface
interface MyFunctionalInterface {

//A method with no parameter


public String sayHello();
}

// lambda expression
MyFunctionalInterface msg = () -> {
return "Hello";
};
System.out.println(msg.sayHello());

Exemplu 2: Java Lambda Expression with single parameter


@FunctionalInterface
interface MyFunctionalInterface {

//A method with single parameter


public int incrementByFive(int a);
}

MyFunctionalInterface f = (num) -> num+5;


System.out.println(f.incrementByFive(22))
Exemplu 3: Iterating Map using for each and Lambda expression
Map<String, Integer> prices = new HashMap<>();
prices.put("Apple", 50);
prices.put("Orange", 20);

prices.forEach((k,v)->System.out.println("Fruit: " + k + ", Price: " + v));

- Method references
Method reference is a shorthand notation of a lambda expression to call a method

If my lambda expression is:


str -> System.out.println(str)
I can replace it with a method reference:
System.out::println

The :: operator is used in method reference to separate the class or object from the method name

1. Method reference to an instance method of an object


@FunctionalInterface
interface MyInterface{
void display();
}

public class Example {


public void myMethod(){
System.out.println("Instance Method");
}

public static void main(String[] args) {


Example obj = new Example();
// Method reference using the object of the class
MyInterface ref = obj::myMethod;

// Calling the method of functional interface


ref.display();
}
}

2. Method reference to a static method of a class

Interface BiFunction<T,U,R>

Type Parameters:

T - the type of the first argument to the function

U - the type of the second argument to the function

R - the type of the result of the function

import java.util.function.BiFunction;

class Multiplication{
public static int multiply(int a, int b){
return a*b;
}
}

public class Example {


public static void main(String[] args) {

BiFunction<Integer, Integer, Integer> product = Multiplication::multiply;

int pr = product.apply(11, 5);

System.out.println("Product of given number is: "+pr);


}
}

3. Method reference to an instance method of an arbitrary object of a particular type


String[] stringArray = { "Steve", "Rick", "Aditya"};

Arrays.sort(stringArray, String::compareToIgnoreCase);

for(String str: stringArray){


System.out.println(str);
}

Old way:
String[] names = { "Steve", "Rick", "Aditya"};

Arrays.sort(names, new Comparator<String>() {


@Override
public int compare(String str1, String str2) {
return str1.compareToIgnoreCase(str2);
}
});
for(String str: names){
System.out.println(str);
}

4. Method reference to a constructor


@FunctionalInterface
interface MyInterface{
Hello display(String say);
}
class Hello{
public Hello(String say){
System.out.print(say);
}
}
public class Example {
public static void main(String[] args) {
//Method reference to a constructor
MyInterface ref = Hello::new;
ref.display("Hello World!");
}
}

Java Functional Interfaces


An interface with only single abstract method is called functional interface.

The functional interface should have Only one abstract method. Along with the one abstract method,
they can have any number of default and static methods.

To use lambda expression in Java, you need to either create your own functional interface or use the pre
defined functional interface provided by Java

Example 1: Creating your own functional interface


@FunctionalInterface
interface MyFunctionalInterface {

public int addMethod(int a, int b);


}
public class BeginnersBookClass {

public static void main(String args[]) {

// lambda expression
MyFunctionalInterface sum = (a, b) -> a + b;
System.out.println("Result: "+sum.addMethod(12, 100));
}
}

Example 2: Using predefined functional interface


import java.util.function.IntBinaryOperator;

public class BeginnersBookClass {

public static void main(String args[]) {

// lambda expression
IntBinaryOperator sum = (a, b) -> a + b;
System.out.println("Result: " + sum.applyAsInt(12, 100));

}
}

Interface Changes – default method and static method

Java 8 allows the interfaces to have default and static methods.

The reason we have default methods in interfaces is to allow the developers to add new methods to the
interfaces without affecting the classes that implements these interfaces.

Reason:

If we add a new method to the XYZInterface, we have to change the code in all the classes that
implements the interface. Maybe we talk about hundreds of classes.

Static methods in interfaces are similar to the default methods except that we cannot override these
methods in the classes that implements these interfaces.

Example: Default method in Interface


interface MyInterface{

/* This is a default method so we need not


* to implement this method in the implementation
* classes
*/
default void newDefaultMethod(){
System.out.println("Newly added default method");
}

/* Already existing public and abstract method


* We must need to implement this method in
* implementation classes.
*/
void existingMethod(String str);
}

public class Example implements MyInterface{

// implementing abstract method


public void existingMethod(String str){
System.out.println("String is: "+str);
}

public static void main(String[] args) {


Example obj = new Example();

//calling the default method of interface


obj.newDefaultMethod();

//calling the abstract method of interface


obj.existingMethod("Java 8 is easy to learn");

}
}

Example 2: Static method in Interface

Since these methods are static, we cannot override them in the implementation classes.
Similar to default methods, we need to implement these methods in
implementation classes so we can safely add them to the existing interfaces.
!!! Nu am inteles cele 2. Nu se bat cap in cap?

interface MyInterface{
default void newMethod(){
System.out.println("Newly added default method");
}

static void anotherStaticMethod(){


System.out.println("Newly added static method");
}

void existingMethod(String str);


}

public class Example implements MyInterface{


// implementing abstract method
public void existingMethod(String str){
System.out.println("String is: "+str);
}
public static void main(String[] args) {
Example obj = new Example();
//calling the default method of interface
obj.newMethod();

//calling the static method of interface


MyInterface.anotherStaticMethod();

//calling the abstract method of interface


obj.existingMethod("Java 8 is easy to learn");
}
}

Now the differences between interfaces and abstract classes: abstract class can have constructor while
in interfaces we can’t have constructors.

The multiple inheritance problem can occur, when we have two interfaces with the default methods of
same signature.

interface MyInterface{

default void newMethod(){


System.out.println("Newly added default method");
}
}
interface MyInterface2{

default void newMethod(){


System.out.println("Newly added default method");
}
}
public class Example implements MyInterface, MyInterface2{
….. obj.newMethod();
}

To solve this problem, we can implement this method in the implementation class like this:
public class Example implements MyInterface, MyInterface2{
/ /Implementation of duplicate default method
public void newMethod(){
System.out.println("Implementation of default method");
}
Stream API

By using streams we can perform various aggregate operations on the data returned from collections,
arrays, Input/Output operations
List<String> names = new ArrayList<String>();
names.add("Ajeet");
names.add("Negan");
names.add("Aditya");
names.add("Steve");
//Old style
int count = 0;
for (String str : names) {
if (str.length() < 6)
count++;
}

//Using Stream and Lambda expression


long count = names.stream().filter(str->str.length()<6).count();

In the second example, the stream() method returns a stream of all the names, the filter() method
returns another stream of names with length less than 6, the count() method reduces this stream to the
result. All these operations are happening parallelly which means we are able to parallelize the code
with the help of streams. Parallel execution of operations using stream is faster than sequential
execution without using streams.

How to work with Stream in Java

1. Create a stream
2. Perform intermediate operations on the initial stream to transform it into another
3. Perform terminal operation

Java Stream Features

1. Stream does not store the elements.

2. The aggregate operations that we perform on the collection, array, or any data source do not
change the data of the source.

3. All the stream operations are lazy - they are not executed until they are needed.

Example 1: Iterating and displaying selected integers


Stream.iterate(1, count->count+1)
.filter(number->number%3==0)
.limit(6)
.forEach(System.out::println);
Example 2: Concatenating two streams
List<String> alphabets = Arrays.asList("A","B","C");
List<String> names = Arrays.asList("Sansa","Jon","Arya");

//creating two streams from the two lists and concatenating them into one
Stream<String> opstream = Stream.concat(alphabets.stream(), names.stream());
//displaying the elements of the concatenated stream
opstream.forEach(str->System.out.print(str+" "));

Stream Filter
The filter() is an intermediate operation that reads the data from a stream and returns a new stream
after transforming the data based on the given condition.
List<String> names = Arrays.asList("Melisandre","Sansa”);

//Creating the stream of all names


Stream<String> allNames = names.stream();

//Creating another stream by filtering long names using filter()


Stream<String> longNames = allNames.filter(str -> str.length() > 6);

Example 1: Stream filter() and collect()


List<String> names = Arrays.asList("Melisandre","Sansa","Jon");

List<String> longnames = names.stream() // converting the list to stream

.filter(str -> str.length() > 6) //filter the stream to create a new stream

.collect(Collectors.toList()); //collect the final stream and convert toList

longnames.forEach(System.out::println);

Example 2: Stream filter() with multiple conditions


List<String> longnames = names.stream()

.filter(str -> str.length() > 6 && str.length() < 8) //Multiple conditions

.collect(Collectors.toList());

Example 3: Stream filter() and map()


List<Integer> num = Arrays.asList(1,2,3,4,5,6);
List<Integer> squares = num.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squares);
Output: [1, 4, 9, …]

forEach
forEach method to iterate over collections and Streams.

Map<Integer, String> hmap = new HashMap<Integer, String>();


   hmap.put(1, "Monkey");
   hmap.put(2, "Dog");

hmap.forEach((key,value)->System.out.println(key+" - "+value));

hmap.forEach((key,value)->{
   
if(key == 4
|| "Cat".equals(value)) { System.out.println(value); }
});

forEach to iterate a List


List<String> fruits = new ArrayList<String>();
fruits.add("Apple");

//lambda expression in forEach Method


fruits.forEach(str->System.out.println(str));

forEach method to iterate a Stream


List<String> names = new ArrayList<String>();
names.add("Maggie");

names.stream() //creating stream


.filter(f->f.startsWith("M")) //filtering names that starts with M
.forEach(System.out::println); //displaying the stream using forEach

Stream forEachOrdered()

when working with parallel streams, you would always want to use the forEachOrdered() method when
the order matters to you, as this method guarantees that the order of elements would be same as the
source
names.stream() 
.filter(f->f.startsWith("M")) 
.parallel()
.forEachOrdered(n->System.out.println(n));

Stream Collectors Class


Collectors is a final class that extends the Object class.
We are grouping the elements of a list using groupingBy() method of Collectors class and printing the
occurrences of each element in the list:
List<String> names =
Arrays.asList("Jon", "Ajeet", "Steve",
"Ajeet", "Jon", "Ajeet");
Map<String, Long> map =
names.stream().collect(
Collectors.groupingBy(Function.identity(), Collectors.counting())
);
System.out.println(map);

Output: {Steve=1, Jon=2, Ajeet=3}

Stream Collectors example of fetching data as List


studentlist.add(new Student(11,"Jon",22)); 
studentlist.add(new Student(22,"Steve",18));
    

List<String> names = studentlist.stream()


.map(n->n.name)
.collect(Collectors.toList());
System.out.println(names);         

Output: [Jon, Steve]

Collecting Data as Set


//Fetching student data as a Set       
Set<Student> students = studentlist.stream()
.filter(n-> n.id>22)
.collect(Collectors.toSet());
//Iterating Set       
for(Student stu : students) {
System.out.println(stu.id+" "+stu.name+" "+stu.age);

StringJoiner
class StringJoiner

Using this class we can join more than one strings with the specified delimiter, we can also provide
prefix and suffix to the final string while joining multiple strings.

// Passing Hyphen(-) as delimiter


StringJoiner mystring = new StringJoiner("-");

// Joining multiple strings by using add() method


mystring.add("Logan");
mystring.add("Magneto");
System.out.println(mystring);

Output: Logan-Magneto

Adding prefix and suffix to the output String


StringJoiner mystring = new StringJoiner(",", "(", ")");
mystring.add("Negan");
mystring.add("Rick");

Output: (Negan,Rick)

Merging two StringJoiner objects

StringJoiner mystring = new StringJoiner(",", "(", ")");

mystring.add("Negan");
mystring.add("Rick");
System.out.println("First String: "+mystring);

StringJoiner myanotherstring = new StringJoiner("-", "pre", "suff");

myanotherstring.add("Sansa");
myanotherstring.add("Imp");

System.out.println("Second String: "+myanotherstring);

/* Merging both the strings


* The important point to note here is that the output string will be
* having the delimiter prefix and suffix of the first string “(“ and
“)” (the string
* which is calling the merge method of StringJoiner)
*/
StringJoiner mergedString = mystring.merge(myanotherstring);
System.out.println(mergedString);

First String: (Negan,Rick)


Second String: preSansa-Impsuff
(Negan,Rick,Sansa-Imp)

Optional Class
Optional class in java.util package. This class is introduced to avoid NullPointerException that we
frequently encounters if we do not perform null checks in our code.
Old style:
String[] str = new String[10];
//Getting the substring
String str2 = str[9].substring(2, 5);

System.out.print(str2);
Output:
Exception in thread "main" java.lang.NullPointerException
at Example.main(Example.java:5)

Optional.ofNullable() method of the Optional class, returns a Non-empty Optional if the given
object has a value, otherwise it returns an empty Optional.

by using isPresent() method we can check whether the particular Optional object(or instance) is
empty or no-empty.

String[] str = new String[10];     


Optional<String> isNull = Optional.ofNullable(str[9]);       
  if(isNull.isPresent()){     
 //Getting the substring          
 String str2 = str[9].substring(2, 5);         
 //Displaying substring           
System.out.print("Substring is: "+ str2);      
  }     
  else{     
  System.out.println("Can’t get the substr from an empty strng");     
  }   

Arrays Parallel Sort


in the Arrays class a new method parallelSort() introduced to support the parallel sorting of array
elements.

1.The given array is divided into the sub arrays and the sub arrays are further divided into
the their sub arrays, this happens until the sub array reaches a minimum granularity.
2. The sub arrays are sorted individually by multiple threads. The parallel sort uses Fork/Join
Framework for sorting sub arrays parallelly.
3. The sorted sub arrays are merged

The parallelSort() method uses the concept of multithreading which makes it much faster compared to
the normal sort when there are lot of elements.

Sorting Primitive Data types with Parallel Sort


int numbers[] = {22, 89, 1, 32, 19, 5};
//Parallel Sort method for sorting int array
Arrays.parallelSort(numbers);
//converting the array to stream and displaying using forEach
Arrays.stream(numbers).forEach(n->System.out.print(n+" "));

Output: 1 5 19 22 32 89

You might also like