0Pricing
Java Academy · Lesson

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(""));   // true

Used 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"));      // false

Comparator 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 WORLD

Pitfall: 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 value

Performance

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

  1. Static Method References
  2. Instance Method References on a Specific Instance
  3. Arbitrary-Instance Method References
  4. Constructor References
← Back to Java Academy