Back to Method References
Q2Question 2 of 5

Rewrite (a, b) -> a.compareTo(b) as String::compareTo

Key Concepts
  • A two-argument lambda calling an instance method on its first argument can be an unbound method reference
  • The first parameter becomes the receiver; the second becomes the method's argument
  • (a, b) -> a.compareTo(b) becomes String::compareTo
  • This matches Comparator<String> because compare(a, b) maps to a.compareTo(b)
  • For natural ordering, Comparator.naturalOrder() is even clearer than String::compareTo
  • The bound form someString::compareTo fixes the receiver and takes only one argument

Definition

(a, b) -> a.compareTo(b) equals String::compareTo: the first argument is the receiver, the second is the method's argument.
  • An unbound two-argument reference Type::instanceMethod maps the first parameter to the receiver and the second to the method argument.
  • String::compareTo satisfies Comparator because compare(a, b) calls a.compareTo(b).
  • The bound form someString::compareTo fixes 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.

JAVA
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);
    }
}
TEXT
[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 |

JAVA
// 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.

JAVA
// 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.

JAVA
// 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?

JAVA
// 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.

JAVA
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

DIAGRAM
  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.

---

Share
Use ← → arrow keys to navigate