Arbitrary-Instance Method References
Reference an instance method without specifying the receiver — used heavily with Stream operations.
Arbitrary-Instance Method References is a free Java Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Are Arbitrary-Instance References?
An arbitrary-instance method reference uses ClassName::instanceMethod. Here the first argument of the functional interface serves as the receiver — each element becomes the object on which the method is called.
import java.util.*;
import java.util.stream.*;
List<String> names = List.of("alice","bob","carol");
// String::toUpperCase — receiver is each list element
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper); // [ALICE, BOB, CAROL]How the Receiver Is Resolved
For an arbitrary-instance reference Type::method, the functional interface's first parameter becomes this (the receiver):
// String::length matches Function<String, Integer>
// Equivalent to: s -> s.length()
Function<String, Integer> len = String::length;
System.out.println(len.apply("Java")); // 4
// String::isEmpty matches Predicate<String>
// Equivalent to: s -> s.isEmpty()
Predicate<String> isEmpty = String::isEmpty;
System.out.println(isEmpty.test("")); // trueUsed Heavily with Stream Operations
Arbitrary-instance references are the most common type in stream pipelines:
List<String> words = List.of("Hello","","World","","Java");
// filter with String::isEmpty
long empty = words.stream().filter(String::isEmpty).count();
System.out.println(empty); // 2
// map with String::toLowerCase
words.stream()
.filter(s -> !s.isEmpty())
.map(String::toLowerCase)
.forEach(System.out::println);BiFunction Style with Two-Arg Methods
A two-argument method on a type matches BiFunction<T, U, R> where T is the receiver and U is the argument:
import java.util.function.*;
// String::startsWith → BiPredicate<String, String>
// receiver=first arg, argument=second arg
BiPredicate<String, String> starts = String::startsWith;
System.out.println(starts.test("JavaStream", "Java")); // true
System.out.println(starts.test("Python", "Java")); // falseComparator with Arbitrary-Instance
String::compareTo matches Comparator<String> — the comparator's two arguments become receiver and parameter:
List<String> words = List.of("banana","apple","cherry");
List<String> sorted = words.stream()
.sorted(String::compareTo)
.collect(Collectors.toList());
System.out.println(sorted); // [apple, banana, cherry]Custom Class Arbitrary-Instance
Works on any class's instance methods, not just JDK types:
record Product(String name, double price) {
boolean isExpensive() { return price > 50; }
String label() { return name + " ($" + price + ")"; }
}
List<Product> products = List.of(
new Product("Widget", 9.99),
new Product("Gadget", 99.99),
new Product("Donut", 1.50)
);
products.stream()
.filter(Product::isExpensive)
.map(Product::label)
.forEach(System.out::println); // Gadget ($99.99)Distinction from Specific-Instance
The key difference:
- Specific:
obj::method— obj is always the receiver - Arbitrary:
Type::method— each element IS the receiver
String fixed = "prefix";
// Specific instance: fixed is always the receiver
Predicate<String> startsFixed = fixed::startsWith; // always checks 'prefix'.startsWith(arg)
// Arbitrary instance: each element is the receiver
Predicate<String> isEmpty = String::isEmpty; // each element.isEmpty()Method References in Collectors
Arbitrary-instance references combine naturally with collectors:
List<String> items = List.of(" hello "," world "," java ");
List<String> trimmed = items.stream()
.map(String::trim)
.collect(Collectors.toList());
System.out.println(trimmed); // [hello, world, java]Chaining Method References
Method references can be chained with andThen/compose on Function:
Function<String, String> pipeline =
((Function<String, String>) String::trim)
.andThen(String::toUpperCase);
System.out.println(pipeline.apply(" hello world "));
// HELLO WORLDPitfall: Overloaded Methods
When a method is overloaded, the compiler must infer which overload matches the functional interface. If ambiguous, it's a compilation error — use a lambda to disambiguate:
// println is overloaded — which one?
// Consumer<String> c = System.out::println; // OK — infers println(String)
// Consumer<Object> c2 = System.out::println; // OK
// But if ambiguous:
// Function<Object,?> fn = System.out::println; // Error — println doesn't return a valuePerformance
Method references and lambdas compile to the same bytecode (via invokedynamic). There is no performance difference — choose whichever is more readable. Method references are preferred by most style guides when they cleanly express intent.
Quick Check
What does String::toUpperCase represent when used with .map()?
Recap: Arbitrary-Instance Method References
Key takeaways:
- Syntax: ClassName::instanceMethod — each element is the receiver
- Matches functional interfaces where receiver is first parameter
- Most common in .map(), .filter(), .sorted(), .forEach()
- Two-arg methods → BiFunction/BiPredicate with element as receiver
- No performance difference vs lambda — choose for readability
Frequently asked questions
Is the “Arbitrary-Instance Method References” lesson free?
Yes — the full text of “Arbitrary-Instance Method References” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “Arbitrary-Instance Method References”?
Reference an instance method without specifying the receiver — used heavily with Stream operations. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Arbitrary-Instance Method References” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Static Method References
- Instance Method References on a Specific Instance
- Arbitrary-Instance Method References
- Constructor References