Skip to content

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

  1. Use when method already exists: Don't create wrappers unnecessarily
  2. Improve readability: Use when it makes code clearer
  3. Static methods: Prefer Math::max over (a, b) -> Math.max(a, b)
  4. Constructors: Use ClassName::new for object creation
  5. 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"));
    }
}