Skip to main content

Java 8 Features Intro

Banner java icon

Java 8 Features with Examples

Java 8 was released in early 2014. This tutorial lists down important Java 8 features with examples such as lambda expressions, Java streams, functional interfaces, default methods, and date-time API changes.

1. Lambda Expressions

Lambda expressions are known to many of us who have worked on other popular programming languages like Scala. In Java programming language, a Lambda expression (or function) is just an anonymous function, i.e., a function with no name and without being bound to an identifier.

Lambda expressions are written precisely where it’s needed, typically as a parameter to some other function.

1.1 Syntax

A few basic syntaxes of lambda expressions are:

(parameters) -> expression
(parameters) -> { statements; }
() -> expression

A typical lambda expression example:

//This function takes two parameters and returns their sum
(x, y) -> x + y

Please note that based on the type of x and y, we may use the method in multiple places. Parameters can match to int, Integer, or simply String. Based on context, it will either add two integers or concatenate two strings.

1.2 Rules for Writing Lambda Expressions

  • A lambda expression can have zero, one, or more parameters.
  • The type of the parameters can be explicitly declared or inferred from the context.
  • Multiple parameters are enclosed in mandatory parentheses and separated by commas.
  • When there is a single parameter, if its type is inferred, it is not mandatory to use parentheses.
  • The body of the lambda expressions can contain zero, one, or more statements.
  • If the body of a lambda expression has a single statement, curly brackets are not mandatory, and the return type of the anonymous function is the same as that of the body expression. If there is more than one statement, curly brackets must be used.

Read More: Java 8 Lambda Expressions Tutorial

2. Functional Interfaces

Functional interfaces are also called Single Abstract Method interfaces (SAM Interfaces). A functional interface permits exactly one abstract method.

Java 8 introduces @FunctionalInterface annotation that we can use to ensure a functional interface follows the contract.

2.1 Functional Interface Example

//Optional annotation
@FunctionalInterface
public interface MyFirstFunctionalInterface {
public void firstWork();
}

A functional interface is valid even if the @FunctionalInterface annotation is omitted. It helps the compiler enforce a single abstract method inside the interface.

Default methods are not abstract, so we can add as many as needed.

If a functional interface overrides one of the public methods of java.lang.Object, it does not count toward the interface’s abstract method count.

Example:

@FunctionalInterface
public interface MyFirstFunctionalInterface {
public void firstWork();

@Override
public String toString();

@Override
public boolean equals(Object obj);
}

Read More: Java 8 Functional Interface Tutorial

3. Default Methods

Java 8 allows non-abstract methods in interfaces using default methods. These methods enable lambda expression functionality.

Default methods help introduce new functionality in interfaces while ensuring binary compatibility with older versions.

Example:

public interface Moveable {
default void move() {
System.out.println("I am moving");
}
}

If a class implements this interface, it can call move() directly:

public class Animal implements Moveable {
public static void main(String[] args) {
Animal tiger = new Animal();
tiger.move();
}
}

Output:

I am moving

If a class wants to customize move(), it can override the method.

Read More: Java 8 Default Methods Tutorial

4. Java 8 Streams

The Streams API in Java 8 provides a mechanism for processing data efficiently, including filtering, transformation, and aggregation.

4.1 Stream API Example

Example of filtering a collection of Strings:

List<String> items = ...; // Initialize the list
String prefix = "str";
List<String> filteredList = items.stream()
.filter(e -> (!e.startsWith(prefix)))
.collect(Collectors.toList());

Read More: Java 8 Internal vs. External Iteration

5. Java 8 Date/Time API Changes

Java 8 introduced a new Date and Time API (JSR-310), also known as ThreeTen, which simplifies date handling.

5.1 Date Classes

  • LocalDate represents a date without time or time-zone.
  • LocalTime represents a time without date or time-zone.
  • LocalDateTime represents a date and time without a time-zone.
  • OffsetDate, OffsetTime, and OffsetDateTime support time-zone offsets.
  • ZoneId is used for working with time zones.

Example:

LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.of(12, 20);
LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"));

5.2 Timestamp and Duration Classes

  • Instant represents an instant in time with nanosecond precision.
  • Duration represents a time difference between two timestamps.

Example:

Instant instant = Instant.now();
Instant instant1 = instant.plus(Duration.ofMillis(5000));
Instant instant2 = instant.minus(Duration.ofMillis(5000));
Instant instant3 = instant.minusSeconds(10);

Duration represents small time units like milliseconds, seconds, minutes, and hours, whereas Period is used for larger time durations:

Duration duration = Duration.ofMillis(5000);
duration = Duration.ofSeconds(60);
duration = Duration.ofMinutes(10);
Period period = Period.ofDays(6);
period = Period.ofMonths(6);
period = Period.between(LocalDate.now(), LocalDate.now().plusDays(60));

Read More: Java 8 Date and Time API Changes

Happy Learning! 🎉