Definition
- An unbound two-argument reference
Type::instanceMethodmaps the first parameter to the receiver and the second to the method argument. String::compareTosatisfiesComparatorbecausecompare(a, b)callsa.compareTo(b).- The bound form
someString::compareTofixes the receiver and leaves only the argument. - For natural ordering,
Comparator.naturalOrder()is clearer and supports null layering.
Priya at the National Bank of India compares two account names alphabetically. Instead of saying 'take name A and ask it to compare itself with name B', she just points at the compareTo skill and lets Java supply the two names in order.
Understanding the Concepts
How does a two-argument method reference work?
The compiler maps the first lambda parameter to the method receiver and the remaining parameters to the method's arguments.
package com.bank.core;
import java.util.*;
public class CompareToRef {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of(
"Priya", "Amit", "Rajesh", "Kavita"));
// Lambda
names.sort((a, b) -> a.compareTo(b));
// Method reference (equivalent)
names.sort(String::compareTo);
System.out.println(names);
}
}
[Amit, Kavita, Priya, Rajesh]
How is unbound different from bound?
| Form | Syntax | Receiver | Arguments |
|---|---|---|---|
| Unbound | String::compareTo | Supplied as first param | Second param |
| Bound | "Rajesh"::compareTo | Fixed to "Rajesh" | Only param |
// Unbound: both arguments come from the comparator
Comparator<String> unbound = String::compareTo;
// compare("Priya", "Amit") → "Priya".compareTo("Amit")
// Bound: receiver is fixed, only one argument needed
java.util.function.ToIntFunction<String> bound = "Rajesh"::compareTo;
// applyAsInt("Amit") → "Rajesh".compareTo("Amit")
When should you use Comparator.naturalOrder() instead?
For natural ordering, Comparator.naturalOrder() states the intent more clearly and supports null layering.
// All three are equivalent for non-null strings:
names.sort(String::compareTo);
names.sort(Comparator.naturalOrder());
names.sort((a, b) -> a.compareTo(b));
// But only naturalOrder supports null handling:
names.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
What are common mistakes?
Mistake: confusing which parameter is the receiver.
// String::compareTo means: first.compareTo(second)
// NOT: second.compareTo(first)
// So this sorts ascending (natural order), not descending.
names.sort(String::compareTo); // ascending
// For descending, use reversed():
names.sort(Comparator.reverseOrder());
What about case-insensitive comparison with method references?
// Case-insensitive sort using an unbound method reference
names.sort(String::compareToIgnoreCase);
// Equivalent lambda
names.sort((a, b) -> a.compareToIgnoreCase(b));
What about using method references with custom objects?
The same pattern applies to any class with a compareTo method.
import java.math.BigDecimal;
List<BigDecimal> balances = new java.util.ArrayList<>(List.of(
new BigDecimal("95000"), new BigDecimal("120000"), new BigDecimal("78000")));
balances.sort(BigDecimal::compareTo); // ascending by value
What should a senior engineer know?
- The unbound form works for any instance method on the type that takes the right number of remaining parameters.
- String::compareTo is natural ascending order. For descending, use Comparator.reverseOrder().
- Method references read best when the method name conveys the intent; compareTo is well-known enough to be clear.
What are common interview follow-up questions?
- "How do you sort case-insensitively?" Use String.CASE_INSENSITIVE_ORDER or String::compareToIgnoreCase.
- "What is the difference between Comparator.naturalOrder() and String::compareTo?" Functionally identical, but naturalOrder() is more intention-revealing.
- "Can you use this with non-Comparable types?" No; compareTo requires the type to implement Comparable.
Visual Overview
Lambda: (a, b) -> a.compareTo(b)
first param 'a' → receiver of compareTo
second param 'b' → argument of compareTo
Method reference: String::compareTo
Compiler maps:
compare(a, b) → a.compareTo(b)
Both satisfy: Comparator<String>
Key Takeaway
When a two-argument lambda calls an instance method on its first argument and passes the second as the method's argument, rewrite it as an unbound method reference. (a, b) -> a.compareTo(b) becomes String::compareTo, where the compiler maps the first parameter to the receiver and the second to compareTo's argument.
At the National Bank of India, this sorts account holder names alphabetically with names.sort(String::compareTo). For natural ordering, Comparator.naturalOrder() is even clearer and supports null layering with nullsFirst.
The key distinction is unbound vs bound: String::compareTo takes two parameters (the receiver and the argument), while "Rajesh"::compareTo fixes the receiver and takes only the argument.
Remember one sentence: String::compareTo replaces (a, b) -> a.compareTo(b), with the first parameter as the receiver and the second as the argument.
---