☕ Java Q122 / 124

Java 8,17 features and with example of each.

AI-Powered Answer ✓ Answered

Java 8 and Java 17 represent significant milestones in the evolution of the Java platform, both being Long Term Support (LTS) releases. Java 8 introduced a paradigm shift with functional programming constructs and enhancements to collections, while Java 17 consolidated many modern language features, enhancing developer productivity and code clarity. This document explores the core features introduced in each version with practical examples.

Java 8 Features

Released in March 2014, Java 8 was a monumental release that brought a major overhaul to the Java language, primarily with the introduction of functional programming capabilities.

Lambda Expressions (JEP 159)

Lambda expressions provide a concise way to represent anonymous functions. They are particularly useful for simplifying code when working with functional interfaces, enabling a more functional programming style.

java
interface Greeting {
    void sayHello(String name);
}

public class LambdaExample {
    public static void main(String[] args) {
        // Before Java 8: Anonymous inner class
        Greeting greetingOld = new Greeting() {
            @Override
            public void sayHello(String name) {
                System.out.println("Hello (old way), " + name + "!");
            }
        };
        greetingOld.sayHello("Alice");

        // With Java 8: Lambda expression
        Greeting greetingLambda = (name) -> System.out.println("Hello (lambda), " + name + "!");
        greetingLambda.sayHello("Bob");

        // Using with a built-in functional interface like Runnable
        new Thread(() -> System.out.println("Hello from a separate thread (lambda)")).start();
    }
}

Stream API (JEP 160)

The Stream API provides a new abstraction for processing sequences of elements. It supports functional-style operations on streams of elements, allowing for declarative, more readable, and often more efficient data processing, especially with collections.

java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamAPIExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Anna");

        // Filter names starting with 'A', convert to uppercase, and collect into a new list
        List<String> filteredNames = names.stream()
                                      .filter(name -> name.startsWith("A"))
                                      .map(String::toUpperCase)
                                      .collect(Collectors.toList());

        System.out.println("Filtered and uppercase names: " + filteredNames); // Output: [ALICE, ANNA]

        // Count names longer than 3 characters
        long count = names.stream()
                          .filter(name -> name.length() > 3)
                          .count();
        System.out.println("Number of names longer than 3 characters: " + count); // Output: 4
    }
}

Default Methods in Interfaces (JEP 174)

Also known as defender methods or virtual extension methods, default methods allow adding new methods to interfaces without breaking existing implementations. This was crucial for evolving the Java Collections API without requiring changes to every implementing class.

java
interface MyInterface {
    void abstractMethod();

    // Default method implementation
    default void defaultMethod() {
        System.out.println("This is a default method implementation.");
    }

    static void staticInterfaceMethod() {
        System.out.println("This is a static method in an interface (Java 8).");
    }
}

class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("Implementing the abstract method.");
    }
}

public class DefaultMethodExample {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.abstractMethod();
        obj.defaultMethod(); // Calling the default method

        MyInterface.staticInterfaceMethod(); // Calling the static interface method
    }
}

Optional Class (JEP 200)

The Optional<T> class is a container object that may or may not contain a non-null value. It provides a way to represent the presence or absence of a value, helping to reduce the number of NullPointerException errors by encouraging explicit null-checking behavior.

java
import java.util.Optional;

public class OptionalExample {
    public static Optional<String> getName(boolean giveName) {
        if (giveName) {
            return Optional.of("Jane Doe");
        } else {
            return Optional.empty(); // Represents no value
        }
    }

    public static void main(String[] args) {
        Optional<String> name1 = getName(true);
        Optional<String> name2 = getName(false);

        // Using isPresent() and get()
        if (name1.isPresent()) {
            System.out.println("Name 1: " + name1.get()); // Output: Name 1: Jane Doe
        } else {
            System.out.println("Name 1 is not present.");
        }

        // Using orElse() to provide a default value
        String actualName2 = name2.orElse("Unknown");
        System.out.println("Name 2 (orElse): " + actualName2); // Output: Name 2 (orElse): Unknown

        // Using ifPresent() to perform an action only if a value is present
        name1.ifPresent(n -> System.out.println("Hello, " + n));
        name2.ifPresent(n -> System.out.println("Hello, " + n)); // This won't print
    }
}

Java 17 Features

Released in September 2021, Java 17 is the latest Long-Term Support (LTS) release after Java 11. It consolidates many preview features introduced in intermediate releases (Java 12-16) into their final forms, making the language more expressive and robust.

Sealed Classes (JEP 409)

Sealed classes and interfaces restrict which other classes or interfaces can extend or implement them. This allows developers to explicitly control the hierarchy of a type, enabling exhaustive checks by the compiler in pattern matching scenarios and providing better architectural control.

java
package com.example.shapes;

public sealed interface Shape permits Circle, Square {
    double area();
}

// Must be final, sealed, or non-sealed
public final class Circle implements Shape {
    private double radius;
    public Circle(double radius) { this.radius = radius; }
    @Override
    public double area() { return Math.PI * radius * radius; }
}

public non-sealed class Square implements Shape {
    private double side;
    public Square(double side) { this.side = side; }
    @Override
    public double area() { return side * side; }
}

// This class is not permitted to implement Shape without being declared in 'permits' clause
// public class Triangle implements Shape { ... } // Compile-time error

public class SealedClassExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape square = new Square(4);

        System.out.println("Circle area: " + circle.area());
        System.out.println("Square area: " + square.area());
    }
}

Pattern Matching for `instanceof` (JEP 406)

Pattern matching for instanceof simplifies the common code pattern of type checking and casting. It eliminates the need for an explicit cast after an instanceof check, making the code more concise and readable.

java
public class PatternMatchingInstanceofExample {
    public static void main(String[] args) {
        Object obj = "Hello Java 17!";

        // Before Java 17
        if (obj instanceof String) {
            String s = (String) obj; // Explicit cast required
            System.out.println("Length (old way): " + s.length());
        }

        // With Java 17 Pattern Matching for instanceof
        if (obj instanceof String s) { // 's' is the pattern variable
            System.out.println("Length (new way): " + s.length());
        }

        Object num = 123;
        if (num instanceof Integer i && i > 100) {
            System.out.println("Integer value greater than 100: " + i);
        }
    }
}

Records (JEP 395)

Records are a new type of class designed to act as transparent carriers for immutable data. They provide a compact syntax for declaring classes that automatically generate boilerplate code like constructors, accessors (getters), equals(), hashCode(), and toString().

java
public record Person(String name, int age) {
    // Records can have custom constructors, static methods, and instance methods
    public String greeting() {
        return "Hi, my name is " + name + " and I am " + age + " years old.";
    }
}

public class RecordExample {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 30);
        Person person2 = new Person("Bob", 25);
        Person person3 = new Person("Alice", 30);

        System.out.println("Person 1: " + person1);
        System.out.println("Person 1 name: " + person1.name()); // Accessor method
        System.out.println("Person 1 age: " + person1.age());
        System.out.println(person1.greeting());

        System.out.println("Person 1 equals Person 2? " + person1.equals(person2)); // Output: false
        System.out.println("Person 1 equals Person 3? " + person1.equals(person3)); // Output: true
    }
}

Text Blocks (JEP 378)

Text blocks are multi-line string literals that simplify the declaration of strings spanning multiple lines, such as code snippets, JSON, XML, or HTML. They remove the need for explicit escape sequences and concatenation, improving readability.

java
public class TextBlockExample {
    public static void main(String[] args) {
        // Before Text Blocks: requires concatenation and escape sequences
        String htmlOld = "<html>\n" +
                         "    <body>\n" +
                         "        <p>Hello, Java!</p>\n" +
                         "    </body>\n" +
                         "</html>";
        System.out.println("--- Old HTML String ---\n" + htmlOld);

        // With Text Blocks: maintains formatting, no need for escapes
        String htmlNew = """
                     <html>
                         <body>
                             <p>Hello, Java!</p>
                         </body>
                     </html>
                     """;
        System.out.println("--- New HTML String (Text Block) ---\n" + htmlNew);

        String json = """
            {
                "name": "Alice",
                "age": 30,
                "city": "New York"
            }
            """;
        System.out.println("--- JSON (Text Block) ---\n" + json);
    }
}