Skip to main content

Method References

Reading Time: 7 Minutes
Difficulty: Beginner


Topic Summary

A method reference is a shorthand for a lambda expression that simply calls an existing method. Instead of writing x -> SomeClass.someMethod(x), you write SomeClass::someMethod. The double-colon :: operator is the method reference operator. It makes code even more concise and readable when the lambda body is just a method call.


What You'll Learn

  • What method references are and when to use them
  • The 4 types of method references with examples
  • How method references relate to lambda expressions
  • How constructor references work

Prerequisites

  • Lambda Expressions (Lesson 1)
  • Functional Interfaces (Lesson 2)

Explanation

What is a Method Reference?

A method reference lets you point to an existing method by name. It's basically a lambda that calls only one method. If your lambda looks like:

x -> SomeClass.someMethod(x)

You can replace it with:

SomeClass::someMethod

Both do the same thing, but the method reference is cleaner and more descriptive.


The 4 Types of Method References

TypeSyntaxLambda Equivalent
Static methodClassName::staticMethodx -> ClassName.staticMethod(x)
Instance method of a specific objectobject::instanceMethodx -> object.instanceMethod(x)
Instance method of an arbitrary objectClassName::instanceMethod(obj, args) -> obj.instanceMethod(args)
Constructor referenceClassName::newargs -> new ClassName(args)

Type 1: Static Method Reference

When your lambda calls a static method:

// Lambda version
Function<String, Integer> parse = s -> Integer.parseInt(s);

// Method reference version
Function<String, Integer> parse = Integer::parseInt;

System.out.println(parse.apply("42")); // 42

More examples:

// Math.abs
Function<Integer, Integer> abs = Math::abs;

// String.valueOf
Function<Integer, String> intToStr = String::valueOf;

// System.out.println — that's a static reference!
Consumer<String> printer = System.out::println;

Type 2: Instance Method of a Specific Object

When your lambda calls an instance method on a specific, known object:

String prefix = "Hello, ";

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

// Method reference version
Function<String, String> greeter = prefix::concat;

System.out.println(greeter.apply("Alice")); // Hello, Alice

Another example:

List<String> names = new ArrayList<>();

// Lambda
Consumer<String> adder = name -> names.add(name);

// Method reference
Consumer<String> adder = names::add;

Type 3: Instance Method of an Arbitrary Object

When your lambda calls an instance method on the parameter itself (not a specific object, but whichever object is passed in):

// Lambda version
Function<String, String> upper = s -> s.toUpperCase();

// Method reference version
Function<String, String> upper = String::toUpperCase;

System.out.println(upper.apply("hello")); // HELLO

Here the method toUpperCase() belongs to String, and it's called on whatever String is passed in. More examples:

// Comparator using String.compareTo
Comparator<String> comp = String::compareTo;

// Predicate using String.isEmpty
Predicate<String> isEmpty = String::isEmpty;

Type 4: Constructor Reference

When your lambda creates a new object with new:

// Lambda version
Supplier<ArrayList<String>> listMaker = () -> new ArrayList<>();

// Constructor reference version
Supplier<ArrayList<String>> listMaker = ArrayList::new;

ArrayList<String> list = listMaker.get();

With arguments:

class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() { return name + " (" + age + ")"; }
}

// BiFunction takes two args and produces an object
BiFunction<String, Integer, Student> creator = Student::new;
Student s = creator.apply("Alice", 20);
System.out.println(s); // Alice (20)

When to Use Method References vs Lambdas

Use a method reference when:

  • The lambda body is just a single method call
  • No modification of the argument(s) is needed
  • Readability improves

Use a lambda when:

  • You need to transform or combine arguments before passing
  • The logic is more than a simple method call
  • You want inline logic
// Use method reference — cleaner
list.stream().map(String::toUpperCase).forEach(System.out::println);

// Use lambda — you need custom logic
list.stream().map(s -> s.toUpperCase() + "!").forEach(System.out::println);

Real-World Analogy

Method references are like speed dial on a phone:

  • Instead of typing the full number every time (lambda), you save it as contact "Mom" and just press "Mom" (method reference).
  • System.out::println is speed dial for "take this and print it to the screen."
  • String::toUpperCase is speed dial for "call toUpperCase on whatever String you give me."

Code Example

Example 1: All 4 Types

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

public class MethodReferencesDemo {
// A static method to demonstrate Type 1
static boolean isLongWord(String s) {
return s.length() > 5;
}

public static void main(String[] args) {
List<String> words = Arrays.asList("Java", "Python", "Go", "Kotlin", "JavaScript");

// Type 1: Static method reference
System.out.println("=== Type 1: Static ===");
List<String> longWords = words.stream()
.filter(MethodReferencesDemo::isLongWord) // static method ref
.collect(Collectors.toList());
System.out.println(longWords); // [Python, Kotlin, JavaScript]

// Type 2: Instance method of a specific object
System.out.println("\n=== Type 2: Specific Instance ===");
String prefix = "Language: ";
Function<String, String> labelMaker = prefix::concat;
words.stream()
.map(labelMaker)
.forEach(System.out::println);
// Language: Java, Language: Python, ...

// Type 3: Instance method of arbitrary object (parameter itself)
System.out.println("\n=== Type 3: Arbitrary Instance ===");
words.stream()
.map(String::toUpperCase) // method ref on the parameter
.sorted(String::compareTo) // compareTo on arbitrary String
.forEach(System.out::println);
// GO, JAVA, JAVASCRIPT, KOTLIN, PYTHON

// Type 4: Constructor reference
System.out.println("\n=== Type 4: Constructor ===");
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Convert list of Strings to list of StringBuilders
List<StringBuilder> builders = names.stream()
.map(StringBuilder::new) // constructor reference
.collect(Collectors.toList());
builders.forEach(sb -> System.out.println(sb.reverse())); // reverse each
// ecilA, boB, eilrahC
}
}

Output

=== Type 1: Static ===
[Python, Kotlin, JavaScript]

=== Type 2: Specific Instance ===
Language: Java
Language: Python
Language: Go
Language: Kotlin
Language: JavaScript

=== Type 3: Arbitrary Instance ===
GO
JAVA
JAVASCRIPT
KOTLIN
PYTHON

=== Type 4: Constructor ===
ecilA
boB
eilrahC

Example 2: Practical Usage with Sorting and Printing

import java.util.*;

public class MethodRefPractical {
public static void main(String[] args) {
List<String> students = Arrays.asList("Zara", "Alice", "Mike", "Bob");

// Sort using method reference
students.sort(String::compareTo);

// Print each using method reference
students.forEach(System.out::println);

System.out.println("---");

// Convert marks to pass/fail
List<Integer> marks = Arrays.asList(45, 82, 33, 91, 55);
marks.stream()
.map(MethodRefPractical::gradeFor) // static method ref
.forEach(System.out::println);
}

static String gradeFor(int mark) {
if (mark >= 80) return "A";
if (mark >= 60) return "B";
if (mark >= 40) return "C";
return "F";
}
}

Output

Alice
Bob
Mike
Zara
---
C
A
F
A
C

Common Mistakes

  • Mistake: Writing obj::method() with parentheses → ✅ Fix: Method references don't have parentheses: obj::method not obj::method()
  • Mistake: Using a method reference when the lambda modifies the argument → ✅ Fix: If you need to do s -> s.trim().toUpperCase(), that's two steps — use a lambda, not a single method reference
  • Mistake: Confusing Type 2 and Type 3 — both use ClassName::method sometimes → ✅ Fix: Type 2 uses a specific instance variable (myObj::method); Type 3 uses the class name when the method is called on the input parameter itself (String::toUpperCase)
  • Mistake: Using constructor reference when constructor doesn't match the functional interface → ✅ Fix: The constructor's parameters must match the functional interface's method signature

Best Practices

  • Use method references whenever a lambda body is just a single method call — it's cleaner
  • System.out::println is one of the most common and useful method references
  • Use constructor references (ClassName::new) with stream map() to create objects from data
  • Don't force method references if they make the code harder to read — lambdas are fine too
  • Combine method references with Stream API for clean, fluent data processing pipelines

Interview Questions

Q: What is a method reference in Java?
A: A method reference is a shorthand syntax for a lambda expression that calls an existing method. It uses the :: operator. For example, String::toUpperCase is equivalent to s -> s.toUpperCase().

Q: What are the 4 types of method references?
A: (1) Static: ClassName::staticMethod, (2) Instance of specific object: object::instanceMethod, (3) Instance of arbitrary object: ClassName::instanceMethod, (4) Constructor: ClassName::new.

Q: What is the difference between a method reference and a lambda?
A: Both implement functional interfaces. A method reference is simply a cleaner syntax for lambdas that only call a single method without modification. Lambdas are more flexible — they can contain any expression or block of code.

Q: Can you use method references for constructors?
A: Yes. Constructor references use the syntax ClassName::new. The functional interface's method signature must match the constructor's parameters. For example, ArrayList::new works as a Supplier<ArrayList>.


Quick Revision

✔ Method reference = shorthand for lambda that calls one method: Class::method
✔ Type 1 — Static: Integer::parseInt
✔ Type 2 — Specific instance: myObj::method
✔ Type 3 — Arbitrary instance (on parameter): String::toUpperCase
✔ Type 4 — Constructor: ArrayList::new
✔ Use when lambda body is just one method call — otherwise use lambda


  • Lambda Expressions
  • Functional Interfaces
  • Stream API
  • Constructor in Java

Next Lesson

Lesson 7 — Date and Time API