sketchboard blog

Examples of Java syntax-related features since Java 8

This blog post provides a comprehensive overview of the Java syntax-related features that have been introduced since Java 8. It covers topics such as lambda expressions, method references, default methods, type annotations, local variable type inference, and more. The post explains each feature in detail and provides practical examples to help readers understand how to use them in their own Java programming. Whether you’re a seasoned Java developer or just getting started, this post will help you stay up-to-date with the latest syntax-related features in Java.

Lambdas and Streams

Lambdas are anonymous functions that can be used to create concise and readable code. Streams provide a powerful way to manipulate collections of objects. Together, they enable functional programming techniques in Java.

// Example of using Lambdas and Streams to filter a list of numbers
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
System.out.println(evenNumbers); // Output: [2, 4]

Optional

The Optional class provides a way to represent a value that may or may not be present. This can help avoid null pointer exceptions and make code more robust.

// Example of using Optional to get a value from a map
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
Optional<String> optionalValue = Optional.ofNullable(map.get("key2"));
if (optionalValue.isPresent()) {
    System.out.println(optionalValue.get());
} else {
    System.out.println("Value not present");
}

Default methods in interfaces

Interfaces can now have default methods, which provide a way to add new functionality to an interface without breaking existing implementations.

// Example of a default method in an interface
interface Animal {
    void makeSound();
    
    default void move() {
        System.out.println("The animal is moving");
    }
}

class Dog implements Animal {
    public void makeSound() {
        System.out.println("Woof!");
    }
}

Dog dog = new Dog();
dog.makeSound(); // Output: Woof!
dog.move(); // Output: The animal is moving

Type Inference

Java now supports type inference, which allows you to declare variables without specifying their type explicitly. The compiler infers the type based on the value being assigned to the variable.

// Example of using type inference with the var keyword
var list = new ArrayList<String>();
list.add("Hello");
list.add("World");
System.out.println(list); // Output: [Hello, World]

Method References

Method references provide a way to refer to a method by its name, without invoking it. This can be useful for passing methods as arguments to other methods or for creating lambda expressions.

// Example of using method references to sort a list of strings
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
list.sort(String::compareToIgnoreCase);
System.out.println(list); // Output: [Apple, Banana, Orange]

Diamond Operator

The diamond operator allows you to omit the type arguments when creating a generic object, if the type can be inferred from the context.

// Example of using the diamond operator to create a generic list
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
System.out.println(list); // Output: [Hello, World]

Try-With-Resources

The try-with-resources statement makes it easier to manage resources that need to be closed after use, such as input/output streams or database connections. It automatically closes the resources at the end of the statement.

// Example of using try-with-resources to read a file
try (BufferedReader reader = new BufferedReader(new FileReader("filename.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.err.println("Error reading file");
}

Multi-Catch

Java now allows you to catch multiple exceptions in a single catch block, using the pipe (|) character to separate the exceptions.

// Example of using multi-catch to handle two exceptions in a single catch block
try {
    // some code that may throw an exception
} catch (IOException | SQLException e) {
    System.err.println("Error: " + e.getMessage());
}

Enhanced for-Loop

Java’s enhanced for-loop allows you to iterate over a collection of objects without the need for an explicit iterator. It makes the code more concise and readable.

// Example of using the enhanced for-loop to iterate over an array
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
    System.out.println(number);
}

Switch Expressions

Java’s switch statement has been enhanced to support switch expressions, which allow you to use a switch statement as an expression, rather than just a statement. This makes the code more concise and easier to read.

// Example of using switch expressions to assign a value based on a condition
int value = switch (condition) {
    case "A" -> 1;
    case "B" -> 2;
    case "C" -> 3;
    default -> 0;
};
System.out.println(value);

Text Blocks

Text blocks provide a way to write multi-line strings without the need for escape characters. This makes it easier to write code that includes long SQL queries, HTML templates, or JSON data.

// Example of using text blocks to write a multi-line string
String html = """
    <html>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
""";
System.out.println(html);

Records

Records are a new type of class introduced in Java 16. They are designed to be used for simple data classes that just hold data and don’t have any behavior. Records provide a concise syntax for defining classes that consist mainly of data.

// Example of using records to define a class that holds data
public record Person(String name, int age) {}
Person person = new Person("Alice", 30);
System.out.println(person.name() + " is " + person.age() + " years old.");

Sealed Classes and Interfaces

Sealed classes and interfaces are another new feature introduced in Java 16. They provide a way to restrict the subtypes that can extend or implement a class or interface. This can help prevent unexpected behavior and make the code more robust.

// Example of using sealed classes to restrict subtypes
public sealed class Shape permits Circle, Rectangle, Triangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
public final class Triangle extends Shape {}

Pattern Matching for instanceof

Java’s instanceof operator has been enhanced to support pattern matching, which allows you to check if an object is an instance of a class or interface and simultaneously extract its components.

// Example of using pattern matching to check if an object is an instance of a class and extract its components
if (shape instanceof Circle circle) {
    System.out.println("Circle with radius " + circle.getRadius());
} else if (shape instanceof Rectangle rectangle) {
    System.out.println("Rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight());
}

Text Blocks with Formatted Output

Java’s text blocks have been enhanced to support formatted output, which allows you to insert values into a multi-line string using placeholders.

// Example of using text blocks with formatted output to create a multi-line string with placeholders
String message = """
    Dear %s,

    Your order has been received and is being processed.
    We will notify you once it has been shipped.

    Thank you for shopping with us!

    Best regards,
    The Acme Corporation
""".formatted(customerName);
System.out.println(message);

Records with Local Classes

Java 17 introduced the ability to define records as local classes. This allows you to define records inside methods, constructors, or other blocks of code, making it easier to create small data-holding classes.

// Example of using records as local classes to define a small data-holding class
public void processPerson() {
    record Person(String name, int age) {}
    Person person = new Person("Alice", 30);
    System.out.println(person.name() + " is " + person.age() + " years old.");
}

Go ahead and write code snippets on your Sketchboard’s virtual whiteboard. Syntax highlightning is available on note and text shapes.