skip to content

How does StringBuilder's fluent method chaining work, and what does a method like append or reverse return?

level: juniorimportance: should knowfreq 48%

answer

  1. mutating methods return this
  2. return this -> chaining
  3. one builder mutated, not many
  4. toString() = the only new String
  5. reverse/insert change in place

basics

~10 s

Mutating methods like append, insert, and reverse change the builder in place and then return the same builder object (this). Because they return the builder, you can chain calls one after another, like sb.append("a").append("b").reverse().

solid answer

~30 s

StringBuilder's mutating methods (`append`, `insert`, `delete`, `replace`, `reverse`) modify the internal buffer in place and return `this` — a reference to the same builder. Returning `this` enables the **fluent / method-chaining** style: `sb.append(name).append(": ").append(value)` reads left to right and avoids repeating the variable. Each call mutates the one underlying object — chaining is not creating new builders, it is threading the same instance through successive operations. Only `toString()` produces a separate, immutable String. The same chaining works on StringBuffer. A common gotcha: `reverse()` reverses in place and returns the builder, so `sb` itself is now reversed.

code

java · 12 lines
java
StringBuilder sb = new StringBuilder();
// each append returns the same builder, so calls chain:
String out = sb.append("id=")
               .append(42)        // append is overloaded for int
               .append(", ok=")
               .append(true)      // ...and boolean
               .toString();       // only toString() yields a String
// out == "id=42, ok=true"

StringBuilder x = new StringBuilder("abc");
x.reverse();                      // mutates in place
System.out.println(x);            // prints "cba", not "abc"

go deeper

for a junior

Knows you can chain appends and that each returns the builder so calls can follow one another, and that toString() gives the final String.

for a middle

Explains the return-this mechanism, in-place mutation semantics, and append's overloads for different types.

for a senior

Discusses fluent-interface design, distinguishes mutating (return this) from query methods, and the snapshot-before-mutate caution.

for a principal

Frames return-this as a general builder/fluent API pattern, weighs readability vs debuggability of long chains, and API design consistency across the JDK.

## Fluent interfaces / method chaining A **fluent interface** is an API where methods return an object you can immediately call another method on, so calls 'chain' together in one expression. StringBuilder is a classic example. The enabling trick is simple: each mutating method returns **`this`**. `this` is the Java keyword for 'the current object' — the very builder the method was called on. So `append` does two things: it writes the characters into the buffer (the real work) and then returns the same builder so the next call has something to attach to. ```java StringBuilder sb = new StringBuilder(); sb.append("Hello").append(", ").append("world").append('!'); String result = sb.toString(); // "Hello, world!" ``` Every `append` above is called on the *same* `sb`. The chain is equivalent to four separate `sb.append(...)` statements. Chaining is purely syntactic convenience; it does not create extra objects. ## What the methods return - `append(...)`, `insert(...)`, `delete(...)`, `replace(...)`, `reverse()` — all **return the builder itself** (`StringBuilder`/`StringBuffer`), after mutating it. That return is what makes chaining possible. - `toString()` — returns a brand-new immutable **String** snapshot. This is the one that gives you a real String to use elsewhere. - Query methods like `charAt(i)`, `length()`, `indexOf(...)` return values (a char, an int, etc.), not the builder, so they end a chain. ## Watch out: in-place semantics Because the methods mutate the underlying object, the builder's state really changes: ```java StringBuilder sb = new StringBuilder("abc"); sb.reverse(); // sb is now "cba" — the original is gone String r = sb.toString(); // "cba" ``` `reverse()` did not return a separate reversed copy; it reversed `sb` and returned `sb`. If you need both the original and the transformed form, snapshot first (`String original = sb.toString();`) before mutating. ## append accepts many types `append` is **overloaded** (multiple versions with the same name, different parameter types): it accepts `String`, `char`, `int`, `long`, `double`, `boolean`, `Object`, `char[]`, etc., converting each to its text form. So `sb.append(42).append(true)` yields `"42true"`. For `Object`/null it appends `"null"`. ## Summary - Mutating methods return `this`, enabling fluent chaining. - The chain mutates one shared builder; no new builders are made. - `toString()` is the only method that yields a separate immutable String. - `reverse()`/`insert()`/`delete()` change the builder in place — snapshot first if you need the original. - `append` is overloaded for many argument types.

  • What does append return, and why does that matter?
    It returns the same StringBuilder (this). That return value is what lets you chain another call directly onto it, producing the fluent style; it is not a new String.
  • After sb.reverse(), is the original order still available in sb?
    No. reverse() mutates the builder in place, so sb now holds the reversed characters. To keep the original you must snapshot it (e.g. toString()) before calling reverse().

saying these in an interview costs you the question

  • Thinking each chained call creates a new builder.
  • Believing append returns a String (it returns the builder).
  • Assuming reverse() returns a copy and leaves the original unchanged.
  • Forgetting toString() is needed to get an actual String out.

context