opencodez

Learn 7 Most Important Java8 Features with Easy Examples

Itxs been a long time since Java 8 has been introduced. Java 9 and 10 are already becoming the talk of the town. So its time to learn Java 8 Features with some easy Examples.

Below are Most Important 7 Features we are going to learn in this article :

  1. Lambda Expressions
  2. Default Methods
  3. Date and Time API
  4. Streams
  5. forEach() Method
  6. Type Annotations
  7. Repeating Annotations

Lambda Expressions

one of the famous feature introduced in Java 8. Lambdas treat functionality as a method argument or code as data. Lambda expressions allow you to present your code more compactly.

For demonstration let us see a basic sorting example to check lambdas. Let us write a simple class to store some data for Employee:

public class Employee {
	
	private int id;
	private String name;
	private double salary;

	public Employee(int id, String name, double salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	//Getters x Setters
}

Standard Comparator

ComparatorxEmployeex sortByName = new ComparatorxEmployeex() {
	@Override
	public int compare(Employee e1, Employee e2) {
		return e1.getName().compareTo(e2.getName());
	}
};

Lambda Equivalent

ComparatorxEmployeex lambdaSortByName = 
		(Employee e1, Employee e2) -x e1.getName().compareTo(e2.getName());

Sorting

// Initial data
ArrayListxEmployeex list = new ArrayListxEmployeex();
list.add(new Employee(500, "Shifoo", 150000));
list.add(new Employee(504, "Oogway", 120000));
list.add(new Employee(503, "Tigress", 100000));
list.add(new Employee(730, "Mantis", 45000));

System.out.println("Initial List :");
list.forEach(System.out::println);

//sortByName already defined in above snippet
Collections.sort(list, sortByName);
System.out.println("\nStandard Sorted by Name :");
list.forEach(System.out::println);

//lambdaSortByName already defined in above snippet
list.sort(lambdaSortByName);
System.out.println("\nLambda Sorted by Name :");
list.forEach(System.out::println);

ComparatorxEmployeex lambdaSortById = (Employee e1, Employee e2) -x e1.getId() - e2.getId();
list.sort(lambdaSortById);

System.out.println("\nSorted by Id :");
list.forEach(System.out::println);

Output

Initial List :
500, Shifoo, 150000.0
504, Oogway, 120000.0
503, Tigress, 100000.0
730, Mantis, 45000.0

Standard Sorted by Name :
730, Mantis, 45000.0
504, Oogway, 120000.0
500, Shifoo, 150000.0
503, Tigress, 100000.0

Lambda Sorted by Name :
730, Mantis, 45000.0
504, Oogway, 120000.0
500, Shifoo, 150000.0
503, Tigress, 100000.0

Sorted by Id :
500, Shifoo, 150000.0
503, Tigress, 100000.0
504, Oogway, 120000.0
730, Mantis, 45000.0

You can also see adding different comparison criteria is easy and compact. The above example also points out other improvements added in Java 8. But we will discuss them later in the article.

Default Methods

From Java 8 onwards your interfaces can have method implementations as well. These implementations are defined with keyword default. The class implementing the interface can access these methods or they can even override the default methods.

We will define sample interfaces for Printer and Scanner.

public class DefaultMethodsTest {

	public static void main(String args[]) {
		Printer printer = new PrinterAndScanner();
		printer.print();
	}
}

interface Printer {
	default void print() {
		System.out.println("I can print!");
	}
}

interface Scanner {
	default void scan() {
		System.out.println("I can scan!");
	}
}

class PrinterAndScanner implements Printer, Scanner {
	public void print() {
		Scanner.super.scan();
		Printer.super.print();
	}
}

In above, we see that class PrinterAndScanner scans and prints. Thus it combines the functionality of two different types of machines.

The default methods will not break any old instances of the interface.

Date and Time API

Java 8 comes with a new date-time API under the package java.time. The new API is thread safe. Out of whole new classes under this new API, you may want to know few first like LocalDate, LocalTime, LocalDateTime, DateTimeFormatter

// Current Date
LocalDate today = LocalDate.now();
System.out.println("Current Date = " + today);

LocalDate todayNewYork = LocalDate.now(ZoneId.of("America/New_York"));
System.out.println("Current Date in America = " + todayNewYork);

// Current Time
LocalTime time = LocalTime.now();
System.out.println("Current Time = " + time);

LocalTime timeNewYork = LocalTime.now(ZoneId.of("America/New_York"));
System.out.println("Current Time in America = " + timeNewYork);

// Current Date
LocalDateTime now = LocalDateTime.now();
System.out.println("Current DateTime = " + now);

System.out.println(now.format(DateTimeFormatter.ofPattern("dd/MM/YYYY HH:mm:ss")));

Output

Current Date = 2018-02-17
Current Date in America = 2018-02-16
Current Time = 01:58:34.463
Current Time in America = 15:28:34.464
Current DateTime = 2018-02-17T01:58:34.464
17/02/2018 01:58:34

Streams

This is one of the major new features in Java 8. A new package java.util.stream with new functionality which contains classes for processing sequences of elements.

Streams can be created using List, Arrays

String[] arr = new String[]{"P", "A", "V"};
StreamxStringx stream = Arrays.stream(arr);
stream.forEach(System.out::println);

stream = Stream.of("V", "A", "P");
stream.forEach(System.out::println);

ListxStringx list = Arrays.asList("Pavan", "Opencodez");
stream = list.stream();

list.parallelStream().forEach(System.out::println);

The above code snippet also takes you through one of the interesting feature added in Java 8, Parallel Operation. The parallelStream()  method makes sure the elements of a list are handled parallelly, thus improving performance.

Apart from above, Streams comes with vast add-on functionality that will make your life easier if used carefully.

Iterating

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

Filtering

list.stream().filter(element -x element.contains("P"));

Matching 

boolean isValid = list.stream().anyMatch(element -x element.contains("P"));

Collecting

ListxStringx results 
  = list.stream().map(element -x element.toUpperCase()).collect(Collectors.toList());

Read here In-Depth Tutorial on Streams

forEach() Method

As we have already seen default methods above, forEach is default method added to improved Java8 interface Iterable

Iterating over List

ArrayListxEmployeex list = new ArrayListxEmployeex();
list.add(new Employee(500, "Shifoo", 150000));
list.add(new Employee(504, "Oogway", 120000));
list.add(new Employee(503, "Tigress", 100000));
list.add(new Employee(730, "Mantis", 45000));

System.out.println("Printing List with forEach");
list.forEach(employee -x System.out.println(employee));

System.out.println("\nPrinting List after Filtering");
list.stream()
	.filter(employee -x employee.getSalary() x 100000)
	.forEach(System.out::println);

Output

Printing List with forEach
500, Shifoo, 150000.0
504, Oogway, 120000.0
503, Tigress, 100000.0
730, Mantis, 45000.0

Printing List after Filtering
500, Shifoo, 150000.0
504, Oogway, 120000.0

Iterating over Map

MapxInteger, Employeex items = new HashMapxx();
items.put(500,new Employee(500, "Shifoo", 150000));
items.put(504, new Employee(504, "Oogway", 120000));
items.put(503, new Employee(503, "Tigress", 100000));
items.put(730, new Employee(730, "Mantis", 45000));

System.out.println("Printing Map with forEach");
items.forEach((k,v)-xSystem.out.println("Key : " + k + " Value : " + v));

System.out.println("\nPrinting Map with forEach");
items.forEach((k,v)-x{
	System.out.println("Key : " + k + " Value : " + v);
	if("Oogway".equals(v.getName())){
		System.out.println("Hello Master " + v.getName());
	}
});

Output

Printing Map with forEach
Key : 500 Value : 500, Shifoo, 150000.0
Key : 503 Value : 503, Tigress, 100000.0
Key : 504 Value : 504, Oogway, 120000.0
Key : 730 Value : 730, Mantis, 45000.0

Printing Map with forEach
Key : 500 Value : 500, Shifoo, 150000.0
Key : 503 Value : 503, Tigress, 100000.0
Key : 504 Value : 504, Oogway, 120000.0
Hello Master Oogway
Key : 730 Value : 730, Mantis, 45000.0

Type Annotations

With Type Annotations you can apply an annotation anywhere a type is used, not just on a declaration. Type Annotations adds stronger type checking to your Java code. Below are few annotations:

@NonNull – The compiler can determine code path that may receive a null value.

@ReadOnly – The compiler will raise a flat if any attempt to change the object.

@Regex – With this annotation, compiler checks if given string is valid regular expression or not.

Repeating Annotations

Repeating Annotations provide the ability to apply the same annotation type more than once to the same declaration or type use. E.g. is if you want to schedule a taks with multiple triggers you will implement a repeating annotation and use:

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void scheduledTask() { ... }

When we develop a repeating annotation we need to annotate it with @Repeatable meta-annotation.

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

To maintain the backward compatibility, these annotations are implemented along with containing annotation. We will provide a Containing Annotation as:

public @interface Schedules {
    Schedule[] value();
}

You can retrieve the annotations with help of several methods available in the Reflection API. One such method is:

Schedule[] schedules = xYourClassx.class.getAnnotationsByType(Schedule.class);
for (Schedule schedule : schedules) {
	System.out.println(schedule.dayOfMonth());
}

Conclusion

In this article, we tried to learn Java 8 using examples. The code snippets are easy and straightforward. Please feel free to comment or ask a question or two.