:: (double colon) operator in Java 8
Categories:
Mastering the :: (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.
map
, filter
, and forEach
often take functional interfaces as arguments.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