10.2. Method References
Method references are a shorthand notation of lambda expressions to call methods. They make code more readable and concise by referring to methods directly.
Types of Method References
1. Static Method Reference
Syntax: ClassName::staticMethodName
java
import java.util.function.Supplier;
import java.util.function.Function;
public class StaticMethodReference {
public static String getDefaultMessage() {
return "Hello from static method!";
}
public static Integer parseInt(String s) {
return Integer.parseInt(s);
}
public static void main(String[] args) {
// Using static method reference
Supplier<String> messageSupplier = StaticMethodReference::getDefaultMessage;
System.out.println(messageSupplier.get()); // Output: Hello from static method!
// Using predefined static methods
Function<String, Integer> parser = Integer::parseInt;
Integer number = parser.apply("123");
System.out.println("Parsed number: " + number); // Output: Parsed number: 123
}
}
2. Instance Method Reference of Particular Object
Syntax: object::instanceMethodName
java
import java.util.function.Supplier;
import java.util.function.Consumer;
public class InstanceMethodReference {
private String name = "Instance Method";
public String getName() {
return name;
}
public void printMessage(String message) {
System.out.println("Message: " + message);
}
public static void main(String[] args) {
InstanceMethodReference instance = new InstanceMethodReference();
// Instance method reference on specific object
Supplier<String> nameSupplier = instance::getName;
System.out.println(nameSupplier.get()); // Output: Instance Method
Consumer<String> printer = instance::printMessage;
printer.accept("Hello World!"); // Output: Message: Hello World!
}
}
3. Instance Method Reference of Arbitrary Object
Syntax: ClassName::instanceMethodName
java
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Arrays;
import java.util.List;
public class ArbitraryInstanceMethodReference {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Instance method reference on arbitrary object
names.forEach(System.out::println);
// Output:
// Alice
// Bob
// Charlie
// Using String methods
Function<String, Integer> lengthGetter = String::length;
System.out.println("Length: " + lengthGetter.apply("Hello")); // Output: Length: 5
Supplier<Integer> lengthSupplier = "Hello World"::length;
System.out.println("String length: " + lengthSupplier.get()); // Output: String length: 11
}
}
4. Constructor Reference
Syntax: ClassName::new
java
import java.util.function.Supplier;
import java.util.function.Function;
import java.util.ArrayList;
import java.util.List;
public class ConstructorReference {
public static void main(String[] args) {
// Constructor reference for no-arg constructor
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> names = listSupplier.get();
names.add("Alice");
System.out.println("List: " + names); // Output: List: [Alice]
// Constructor reference with parameters
Function<String, Integer> integerCreator = Integer::new;
Integer number = integerCreator.apply("42");
System.out.println("Number: " + number); // Output: Number: 42
// Array constructor reference
Function<Integer, String[]> arrayCreator = String[]::new;
String[] stringArray = arrayCreator.apply(3);
stringArray[0] = "First";
stringArray[1] = "Second";
System.out.println("Array length: " + stringArray.length); // Output: Array length: 3
}
}
Practical Examples
Sorting with Method References
java
import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class SortingWithMethodReferences {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
// Sort by name using method reference
people.sort(Comparator.comparing(Person::getName));
System.out.println("Sorted by name: " + people);
// Sort by age using method reference
people.sort(Comparator.comparing(Person::getAge));
System.out.println("Sorted by age: " + people);
}
}
Factory Pattern with Method References
java
import java.util.function.Supplier;
import java.util.HashMap;
import java.util.Map;
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Circle");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Rectangle");
}
}
public class ShapeFactory {
private static final Map<String, Supplier<Shape>> shapeMap = new HashMap<>();
static {
shapeMap.put("circle", Circle::new);
shapeMap.put("rectangle", Rectangle::new);
}
public static Shape createShape(String shapeType) {
Supplier<Shape> shapeSupplier = shapeMap.get(shapeType.toLowerCase());
if (shapeSupplier != null) {
return shapeSupplier.get();
}
throw new IllegalArgumentException("Unknown shape type: " + shapeType);
}
public static void main(String[] args) {
Shape circle = ShapeFactory.createShape("circle");
circle.draw(); // Output: Drawing Circle
Shape rectangle = ShapeFactory.createShape("rectangle");
rectangle.draw(); // Output: Drawing Rectangle
}
}
Method References vs Lambda Expressions
Lambda Expression
java
Function<String, Integer> parser1 = s -> Integer.parseInt(s);
Supplier<List<String>> supplier1 = () -> new ArrayList<>();
Consumer<String> printer1 = s -> System.out.println(s);
Method Reference (More Readable)
java
Function<String, Integer> parser2 = Integer::parseInt;
Supplier<List<String>> supplier2 = ArrayList::new;
Consumer<String> printer2 = System.out::println;
Best Practices
- Use when method already exists: Don't create wrappers unnecessarily
- Improve readability: Use when it makes code clearer
- Static methods: Prefer
Math::max
over(a, b) -> Math.max(a, b)
- Constructors: Use
ClassName::new
for object creation - Instance methods: Use
object::method
for specific instances
java
import java.util.function.Supplier;
import java.util.function.Function;
public class MethodReferenceBestPractices {
public static void main(String[] args) {
// Good - method reference is clear and concise
Supplier<Double> randomSupplier = Math::random;
Function<String, String> upperCaseConverter = String::toUpperCase;
// Avoid - lambda doesn't add value when method reference exists
Supplier<Double> lessClearSupplier = () -> Math.random();
Function<String, String> lessClearConverter = s -> s.toUpperCase();
System.out.println("Random: " + randomSupplier.get());
System.out.println("Uppercase: " + upperCaseConverter.apply("hello"));
}
}