skip to content

Inside a class, how does an infix member function bind to `this`, and what subtle issue arises when you call an infix member from within the same class without specifying a receiver?

level: seniorimportance: nice to knowfreq 18%

answer

  1. Infix member receiver = the class instance
  2. Inside the class use `this name arg`, not bare `name arg`
  3. `name(arg)` parens form always works
  4. Member infix beats same-name extension infix
  5. When unsure: `this.name(arg)`

basics

~20 s

An infix member's left operand is its receiver. If you call it inside the class without a left operand, you must write this name arg explicitly — bare name arg doesn't work, because infix needs an explicit left side.

solid answer

~40 s

For an infix member `infix fun add(x: Int)`, the call `obj add 5` uses `obj` as the receiver. Inside the same class, `this` is the implicit receiver for normal `.` calls, but the infix form **requires an explicit left operand** — you must write `this add 5`, not `add 5` (which the parser reads as a regular call `add(5)` only if you supply parens, i.e. `add(5)`). So within a member you typically call it as `this add 5` or fall back to `add(5)`. A second subtlety: when an infix member and an infix extension with the same name are both in scope, the **member wins** (member resolution precedence), which can shadow an imported extension unexpectedly. Always know which receiver and which declaration the infix call resolves to.

code

kotlin · 10 lines
kotlin
class Counter(val n: Int) {
    infix fun plusN(m: Int): Counter = Counter(n + m)

    fun doubled(): Counter {
        // return plusN n      // compile error: infix needs explicit left operand
        return this plusN n    // works
    }
}

val c = Counter(2) plusN 3   // receiver Counter(2), arg 3 -> Counter(5)

go deeper

for a junior

Knows the left operand is the receiver and that obj name arg works at a call site.

for a middle

Understands that inside the class you need this name arg and that name(arg) always works.

for a senior

Explains member-over-extension resolution and the shadowing risk between same-named infix declarations.

for a principal

Anticipates resolution/shadowing hazards in API evolution and prescribes explicit this.name(arg) for clarity in shared codebases.

## Receiver binding An infix function always has a **receiver** (the left operand). For a **member** function the receiver is the enclosing class instance: ```kotlin class Vec(val x: Int) { infix fun add(other: Int): Vec = Vec(x + other) } val v = Vec(1) add 5 // receiver = Vec(1), arg = 5 ``` ## Calling an infix member from inside the class Inside a member of `Vec`, `this` is the implicit receiver. But the **infix call form needs an explicit left operand** — you can't drop it: ```kotlin class Vec(val x: Int) { infix fun add(other: Int): Vec = Vec(x + other) fun grow(): Vec { // return add 5 // does NOT compile as infix — no left operand return this add 5 // OK: explicit receiver // return add(5) // OK: ordinary call form } } ``` The takeaway: **`this` is implicit for `.`/parens calls but must be explicit for infix syntax.** ## Member-vs-extension resolution If both an infix **member** and an infix **extension** with the same name are in scope, Kotlin resolves to the **member** (members take priority over extensions). This can silently shadow an imported extension: ```kotlin class Box { infix fun combine(o: Box): Box = this // member } infix fun Box.combine(o: Int): Box = this // extension, different param type // `box combine box2` -> member; `box combine 3` -> extension (by arg type) ``` When the signatures differ only by receiver/specificity, the member generally wins for matching argument types — be deliberate. ## Why it matters - Infix syntax is sugar, but the **explicit left operand** requirement inside a class trips people who expect implicit-`this` behavior. - Shadowing between member and extension infix functions can change which code runs without a compile error. - When unsure, use the unambiguous `this.name(arg)` form.

  • If a class has an infix member and an import brings an infix extension of the same name, which is called?
    The member takes precedence over the extension when the argument matches both; the extension is shadowed. Differing parameter types can route calls to the extension instead.
  • Can you write `add 5` (no receiver) inside the class to invoke the infix member?
    No. Infix syntax requires an explicit left operand, so you write `this add 5`, or use the ordinary `add(5)` call form.

saying these in an interview costs you the question

  • Assuming bare `name arg` works inside the class via implicit `this`
  • Thinking extension infix functions override member ones
  • Not realizing infix needs an explicit left operand
  • Believing infix changes receiver resolution rules
  • Ignoring shadowing between member and extension infix declarations

context