:: (double colon) operator in Java 8

Learn :: (double colon) operator in java 8 with practical examples, diagrams, and best practices. Covers java, java-8, method-reference development techniques with visual explanations.

Mastering the :: (Double Colon) Operator in Java 8

Hero image for :: (double colon) operator in Java 8

Explore Java 8's method reference operator (::) for concise and readable lambda expressions, enhancing functional programming.

Java 8 introduced a wealth of new features that revolutionized how developers write code, especially concerning functional programming. Among these, the :: (double colon) operator, also known as the method reference operator, stands out for its ability to make code more readable and concise. It provides a way to refer to methods or constructors without executing them, serving as a shorthand for certain lambda expressions.

What is the Method Reference Operator?

The method reference operator :: is used to refer to a method without invoking it. It's a compact way to express a lambda expression that simply calls an existing method. This operator helps in making your code more functional and easier to understand, especially when dealing with streams and other functional interfaces. Instead of writing a full lambda expression, you can often use a method reference to achieve the same result with less boilerplate.

flowchart TD
    A[Lambda Expression] --> B{Does it just call an existing method?}
    B -- Yes --> C[Method Reference (::)]
    B -- No --> A
    C --> D[Concise & Readable Code]

Decision flow for using method references over lambda expressions

Types of Method References

There are four main types of method references in Java 8, each serving a specific purpose and providing a concise syntax for different scenarios. Understanding these types is crucial for effectively leveraging the :: operator in your code.

1. Static Method References

A static method reference refers to a static method of a class. The syntax is ClassName::staticMethodName. This is useful when you have a lambda expression that simply calls a static method.

import java.util.Arrays;
import java.util.List;

public class StaticMethodRef {
    public static void printMessage(String message) {
        System.out.println("Static: " + message);
    }

    public static void main(String[] args) {
        List<String> messages = Arrays.asList("Hello", "World", "Java");

        // Lambda expression
        messages.forEach(msg -> StaticMethodRef.printMessage(msg));

        // Equivalent Method Reference
        messages.forEach(StaticMethodRef::printMessage);
    }
}

Example of a static method reference

2. Instance Method References of a Particular Object

This type refers to an instance method of a particular object. The syntax is objectName::instanceMethodName. This is used when the lambda expression calls an instance method on a specific, already-created object.

import java.util.Arrays;
import java.util.List;

public class InstanceMethodRef {
    public void printUpperCase(String text) {
        System.out.println("Instance: " + text.toUpperCase());
    }

    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        InstanceMethodRef printer = new InstanceMethodRef();

        // Lambda expression
        words.forEach(word -> printer.printUpperCase(word));

        // Equivalent Method Reference
        words.forEach(printer::printUpperCase);
    }
}

Example of an instance method reference of a particular object

3. Instance Method References of an Arbitrary Object of a Particular Type

This refers to an instance method where the receiver object is determined at runtime by the context. The syntax is ClassName::instanceMethodName. This is common when the lambda's first parameter is the target object of the method call.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

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

        // Lambda expression
        names.stream().map(name -> name.length()).forEach(System.out::println);

        // Equivalent Method Reference
        names.stream().map(String::length).forEach(System.out::println);

        // Another example with a custom functional interface
        Function<String, String> toUpperCaseLambda = s -> s.toUpperCase();
        Function<String, String> toUpperCaseMethodRef = String::toUpperCase;

        System.out.println(toUpperCaseLambda.apply("hello"));
        System.out.println(toUpperCaseMethodRef.apply("world"));
    }
}

Example of an arbitrary instance method reference

4. Constructor References

A constructor reference refers to a constructor. The syntax is ClassName::new. This is useful when you need to create new objects within a stream or other functional operations.

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

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + '}';
    }
}

public class ConstructorRef {
    public static void main(String[] args) {
        // Constructor reference for a no-arg constructor (if Person had one)
        // Supplier<Person> personSupplier = Person::new;
        // Person p = personSupplier.get();

        // Constructor reference for a constructor with arguments
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // Lambda expression
        List<Person> peopleLambda = names.stream()
                                        .map(name -> new Person(name))
                                        .collect(Collectors.toList());

        // Equivalent Method Reference
        List<Person> peopleMethodRef = names.stream()
                                         .map(Person::new)
                                         .collect(Collectors.toList());

        System.out.println(peopleLambda);
        System.out.println(peopleMethodRef);
    }
}

Example of a constructor reference