CoreJava Java8

Java 8 – Method References Explained with Examples

Java 8 – Method References Explained with Examples

A method reference is a compact way to write a lambda that does nothing but call an existing method. Instead of writing x -> SomeClass.someMethod(x), you write SomeClass::someMethod. Method references make code shorter and more readable when the lambda body is just a single method call.

Four Types of Method References

Type Syntax Lambda Equivalent
Static method ClassName::staticMethod x -> ClassName.staticMethod(x)
Instance method of a specific object instance::instanceMethod x -> instance.instanceMethod(x)
Instance method of an arbitrary object of a given type ClassName::instanceMethod x -> x.instanceMethod()
Constructor ClassName::new (args) -> new ClassName(args)

Type 1 – Static Method Reference

import java.util.*;
import java.util.stream.*;
import java.util.function.*;

public class StaticMethodRef {

    // A static method to use as a reference
    public static int square(int n) {
        return n * n;
    }

    public static boolean isEven(int n) {
        return n % 2 == 0;
    }

    public static void main(String[] args) {

        // Lambda version
        Function<Integer, Integer> squareLambda = n -> square(n);

        // Method reference — equivalent
        Function<Integer, Integer> squareRef = StaticMethodRef::square;

        System.out.println(squareLambda.apply(5));  // 25
        System.out.println(squareRef.apply(5));     // 25

        // With Stream
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);

        List<Integer> squares = numbers.stream()
            .map(StaticMethodRef::square)   // method reference
            .collect(Collectors.toList());
        System.out.println(squares);  // [1, 4, 9, 16, 25, 36, 49, 64]

        List<Integer> evens = numbers.stream()
            .filter(StaticMethodRef::isEven)
            .collect(Collectors.toList());
        System.out.println(evens);    // [2, 4, 6, 8]

        // Built-in static method references
        List<String> strings = Arrays.asList("3", "1", "4", "1", "5");
        List<Integer> parsed = strings.stream()
            .map(Integer::parseInt)      // Integer.parseInt(s)
            .collect(Collectors.toList());
        System.out.println(parsed);  // [3, 1, 4, 1, 5]
    }
}

Type 2 – Instance Method of a Specific Object

import java.util.function.*;

public class InstanceSpecificRef {

    public static void main(String[] args) {

        String prefix = "Hello, ";

        // Lambda
        Function<String, String> greetLambda = name -> prefix.concat(name);

        // Method reference — refers to concat() on the specific string object 'prefix'
        Function<String, String> greetRef = prefix::concat;

        System.out.println(greetLambda.apply("Ravi"));  // Hello, Ravi
        System.out.println(greetRef.apply("Java 8"));   // Hello, Java 8

        // Common example: System.out.println
        Consumer<String> printer = System.out::println;
        printer.accept("Printing via method reference");

        // StringBuilder example
        StringBuilder sb = new StringBuilder();
        Consumer<String> appender = sb::append;
        appender.accept("Java ");
        appender.accept("8");
        System.out.println(sb.toString());  // Java 8

        // forEach with instance method reference
        java.util.List<String> names = java.util.Arrays.asList("Alice", "Bob", "Charlie");
        names.forEach(System.out::println);
    }
}

Type 3 – Instance Method of an Arbitrary Object (Most Common)

import java.util.*;
import java.util.stream.*;
import java.util.function.*;

public class InstanceArbitraryRef {

    public static void main(String[] args) {

        // String::toUpperCase — lambda: s -> s.toUpperCase()
        Function<String, String> upper = String::toUpperCase;
        System.out.println(upper.apply("lambda"));  // LAMBDA

        // String::length — lambda: s -> s.length()
        Function<String, Integer> length = String::length;

        // String::compareTo — lambda: (a, b) -> a.compareTo(b)
        Comparator<String> comparator = String::compareTo;

        List<String> words = Arrays.asList("banana", "apple", "cherry", "date");

        // Using method references with streams
        words.stream()
             .map(String::toUpperCase)
             .sorted(String::compareTo)
             .forEach(System.out::println);
        // APPLE, BANANA, CHERRY, DATE

        // startsWith — Predicate via BiPredicate-like usage
        Predicate<String> startsWithA = s -> s.startsWith("A");
        long count = words.stream()
            .filter(s -> s.startsWith("a") || s.startsWith("A"))
            .count();
        System.out.println("Count: " + count);  // 1 (apple)

        // Custom class example
        List<Product> products = Arrays.asList(
            new Product("Laptop",     75000),
            new Product("Smartphone", 25000),
            new Product("Keyboard",    2500)
        );

        // Method reference to instance method getName()
        products.stream()
                .map(Product::getName)         // lambda: p -> p.getName()
                .forEach(System.out::println); // Laptop, Smartphone, Keyboard

        // Sort by price using getter reference
        products.stream()
                .sorted(Comparator.comparingDouble(Product::getPrice))
                .map(Product::getName)
                .forEach(System.out::println);
        // Keyboard, Smartphone, Laptop
    }
}

class Product {
    private String name;
    private double price;
    Product(String name, double price) { this.name = name; this.price = price; }
    public String getName()  { return name; }
    public double getPrice() { return price; }
}

Type 4 – Constructor Reference

import java.util.function.*;
import java.util.*;
import java.util.stream.*;

class Person {
    private String name;
    private int    age;

    Person(String name) { this.name = name; this.age = 0; }
    Person(String name, int age) { this.name = name; this.age = age; }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class ConstructorRef {

    public static void main(String[] args) {

        // Lambda — create Person from String
        Function<String, Person> createLambda = name -> new Person(name);

        // Constructor reference — equivalent
        Function<String, Person> createRef = Person::new;

        Person p1 = createLambda.apply("Alice");
        Person p2 = createRef.apply("Bob");
        System.out.println(p1);  // Alice (0)
        System.out.println(p2);  // Bob (0)

        // Two-argument constructor
        BiFunction<String, Integer, Person> createWithAge = Person::new;
        Person p3 = createWithAge.apply("Charlie", 30);
        System.out.println(p3);  // Charlie (30)

        // Constructor reference with Stream — convert names to Person objects
        List<String> names = Arrays.asList("David", "Eve", "Frank");
        List<Person> people = names.stream()
            .map(Person::new)
            .collect(Collectors.toList());
        people.forEach(System.out::println);
        // David (0), Eve (0), Frank (0)

        // Supplier — no-arg constructor reference
        Supplier<ArrayList<String>> listSupplier = ArrayList::new;
        ArrayList<String> list = listSupplier.get();
        list.add("Java 8");
        System.out.println(list);  // [Java 8]
    }
}

Method References vs Lambda – When to Use Which

Use Method Reference when… Use Lambda when…
The lambda body is a single method call The body has logic beyond a single method call
The method already exists with the right signature You need to combine expressions or add conditions
Using built-in methods like System.out::println, String::length Capturing local variables
// Prefer method reference — just calls one method
names.forEach(System.out::println);
list.stream().map(String::toUpperCase);

// Keep as lambda — has logic
list.stream().filter(s -> s.length() > 3 && s.startsWith("J"));
list.stream().map(s -> s.trim().toLowerCase() + "!");

Summary

Method references are shorthand for lambdas whose entire body is a single method call. The four types are: static method (Class::method), instance method on a specific object (instance::method), instance method on an arbitrary object of a type (Type::method), and constructor (Type::new). Use them to make stream pipelines and forEach calls cleaner and more readable. When in doubt, write the lambda first, then see if a method reference makes it clearer — never force one if it adds confusion.

Topics: CoreJava Java8
← Newer Post Older Post →

Comments

https://www.blogger.com/comment/frame/6690124484600543990?po=3218746504797505158&hl=en&saa=85391&origin=https://www.java9r.com