Back to Method References
Q5Question 5 of 5

Use an instance method reference on a specific object System.out::println for forEach

Key Concepts
  • System.out::println is a bound instance-method reference: the receiver (System.out) is fixed at creation time
  • It serves as a Consumer<T>, printing each element passed by forEach
  • Bound references capture a specific object; unbound references (String::length) supply the receiver at call time
  • System.out::println is fine for debugging but not for production logging
  • In production, use a Logger reference like logger::info as the Consumer
  • The bound form captures the receiver once and reuses it for every element

Definition

System.out::println is a bound reference: the receiver is the specific System.out object, so forEach prints each element.
  • A bound instance-method reference instance::method captures a specific receiver object.
  • System.out::println captures the System.out PrintStream as the receiver.
  • It serves as Consumer: forEach(System.out::println) prints every element.
  • Bound = receiver is fixed. Unbound (String::length) = receiver is supplied per call.
  • Fine for debugging; use a proper Logger in production.

Priya at the National Bank of India has one specific printer at her desk. Rather than saying 'take an item and send it to this printer', she just points at the printer's print action. Every item goes to that same printer.

Understanding the Concepts

How does a bound instance-method reference work?

The receiver is captured when the reference is created. Only the method's arguments come from the stream.

JAVA
package com.bank.demo;

import java.util.List;

public class BoundRefDemo {
    public static void main(String[] args) {
        List<String> names = List.of("Rajesh", "Priya", "Amit");

        // Lambda
        names.forEach(name -> System.out.println(name));

        // Bound method reference (equivalent)
        names.forEach(System.out::println);
    }
}
TEXT
Rajesh
Priya
Amit

How is bound different from unbound?

| Type | Syntax | Receiver | Stream element provides |
|---|---|---|---|
| Bound | System.out::println | Fixed (System.out) | Method argument |
| Unbound | String::length | Supplied per call | Is the receiver |

JAVA
// Bound: System.out is the receiver, element is the argument
names.forEach(System.out::println);
// Each call: System.out.println("Rajesh"), System.out.println("Priya"), ...

// Unbound: element IS the receiver
names.stream().map(String::toUpperCase);
// Each call: "Rajesh".toUpperCase(), "Priya".toUpperCase(), ...

Why is System.out::println wrong for production?

System.out writes directly to the console with no log levels, no formatting, and no way to disable it.

JAVA
// WRONG: production code
accounts.forEach(System.out::println);

// CORRECT: use a Logger
import java.util.logging.Logger;
Logger log = Logger.getLogger("com.bank.hr");
accounts.forEach(a -> log.info(a.toString()));

// Or with a bound reference on the logger:
accounts.stream()
    .map(Object::toString)
    .forEach(log::info);

Can you use bound references with other objects?

Yes. Any object's method can be captured.

JAVA
StringBuilder sb = new StringBuilder();
names.forEach(sb::append);  // appends each name to the same builder
System.out.println(sb);  // RajeshPriyaAmit

What are common mistakes?

Mistake: assuming System.out::println works with formatted output.

JAVA
// System.out::println prints each element on its own line
// For formatted output, use a lambda:
names.forEach(n -> System.out.printf("Name: %s%n", n));

What should a senior engineer know?

  • The bound reference captures the receiver once at creation time and reuses it for every call.
  • If the receiver changes between creation and use, you get the object as it was at capture time.
  • System.out::println is the most common debugging pattern but should never appear in committed code.
  • Bound references apply broadly: logger::info, list::add, builder::append.

What are common interview follow-up questions?

DIAGRAM
- "What is the difference between System.out::println and x -> System.out.println(x)?" Functionally identical; the reference is more concise.
- "Can you capture a method reference to a local variable?" Yes, as long as the variable is effectively final.
- "What happens if System.out is reassigned?" The reference captured the original object; the reassignment has no effect on the reference.

Visual Overview

DIAGRAM
  Bound reference: System.out::println

  Capture:
    receiver = System.out  (fixed at creation)

  forEach call:
    element "Rajesh" → System.out.println("Rajesh")
    element "Priya"  → System.out.println("Priya")
    element "Amit"   → System.out.println("Amit")

  Compare with unbound (String::length):
    element "Rajesh" → "Rajesh".length()  (element IS the receiver)

Key Takeaway

A bound instance-method reference fixes the receiver at the moment the reference is created. System.out::println captures the System.out PrintStream, so forEach passes each element to that specific object's println method, printing every element on its own line.

The distinction is bound vs unbound: in the bound form the receiver is already chosen and only the method argument comes from the stream. In the unbound form (like String::length), the stream element itself becomes the receiver.

System.out::println is perfect for quick debugging but wrong for production. At the National Bank of India, production code uses a Logger bound reference like log::info instead. The concept is the same: fix the receiver, feed it elements.

Remember one sentence: System.out::println is a bound reference with a fixed receiver, fine for debugging but replaced by a Logger reference in production.

---

Share
Use ← → arrow keys to navigate