Explain the `invoke` operator (`a()`) and the `contains` operator (`x in a`). What are common, idiomatic uses of each?
answer
- `a()` -> `a.invoke()`
- Lambdas are Function types with `invoke`
- `x in a` -> `a.contains(x)` (receiver = right)
- `!in` negates contains
- for-loop `in` uses iterator(), not contains
basics
~10 sIf a type has an operator fun invoke, you can call its instance like a function: obj(). The in operator checks membership: x in a calls a.contains(x) and returns true or false.
solid answer
~40 s`invoke` lets an object be called like a function: `a(args)` -> `a.invoke(args)`. This is how lambdas/`Function` types are actually called under the hood, and it lets you build callable objects, factories, or DSL builders that read like function calls. `contains` backs the `in` operator: `x in a` -> `a.contains(x)` returning `Boolean`; `x !in a` is the negation. The receiver of `contains` is the **collection/range on the right**, not the element. Standard library uses include `Collection.contains`, `String.contains`, `ClosedRange.contains` (so `5 in 1..10` works), and `Map.contains` checks keys. Both are ordinary `operator` functions, can be extensions, and `invoke` may be overloaded with multiple arities/signatures.
code
kotlin · 5 linesclass Range2D(val xs: IntRange, val ys: IntRange) {
operator fun contains(p: Pair<Int, Int>) = p.first in xs && p.second in ys
}
val grid = Range2D(0..9, 0..9)
println((3 to 4) in grid) // grid.contains(3 to 4) -> truego deeper
Knows a() calls invoke and x in a tests membership.
Implements both operators, knows receiver of contains is the right operand, and gives idiomatic uses.
Connects invoke to function-type interfaces and uses it for DSLs/factories; distinguishes membership in from iteration in.
Judges when callable objects/DSLs improve API clarity vs. magic, and standardizes patterns (e.g. companion invoke factories) for the team.
## The `invoke` operator — calling an object When a type declares `operator fun invoke(...)`, instances of that type can be **called with parentheses**: - `a()` -> `a.invoke()` - `a(x, y)` -> `a.invoke(x, y)` This is not a special case for lambdas only — Kotlin's function types (`(Int) -> String` etc.) are interfaces (`Function1`, ...) whose single method is `invoke`. So calling a lambda `f(3)` is literally `f.invoke(3)`. ### Idiomatic uses ```kotlin object Logger { operator fun invoke(msg: String) = println("[LOG] $msg") } Logger("started") // reads like a function call // Factory / smart constructor pattern class User private constructor(val name: String) { companion object { operator fun invoke(name: String) = User(name.trim()) } } val u = User(" bob ") // looks like a constructor, runs custom logic ``` `invoke` can be overloaded with several signatures and arities, and can be an extension function, so you can make a third-party type callable. ## The `contains` operator — membership with `in` The `in` operator maps to `contains`, and crucially **the receiver is the right operand**: - `x in a` -> `a.contains(x)` - `x !in a` -> `!a.contains(x)` `contains` must return `Boolean` and be declared `operator`. ```kotlin class IntBox(private val items: Set<Int>) { operator fun contains(value: Int): Boolean = value in items } val box = IntBox(setOf(1, 2, 3)) println(2 in box) // box.contains(2) -> true println(9 !in box) // !box.contains(9) -> true ``` ### Standard-library examples - `5 in 1..10` -> `(1..10).contains(5)` via `ClosedRange.contains`. - `"b" in "abc"` -> substring/char membership. - `key in map` -> `map.containsKey(key)` semantics (checks keys, not values). - `e in list` -> `list.contains(e)`. ## Distinguishing `in` the operator from `in` the loop The `in` in `for (x in collection)` is **iteration** (driven by `iterator()`), not the `contains` operator. Same keyword, different mechanism. Only the boolean-membership `x in a` uses `contains`. ## Why these matter `invoke` enables fluent DSLs and callable abstractions while keeping call sites clean; `contains` makes membership tests read like natural language and powers ranges.
- When you call a lambda `f(42)`, what operator is actually invoked?`invoke`. Function types are interfaces (`Function1`, etc.) whose method is `operator fun invoke`, so `f(42)` is `f.invoke(42)`.
- For `x in a`, which object is the receiver of `contains`?`a`, the right operand. It desugars to `a.contains(x)`, so the container/range, not the element, owns the function.
invoke is giving an object a doorbell so guests can 'ring' it like a function; in is asking the container, not the item, 'do you hold this?'
saying these in an interview costs you the question
- Saying `x in a` calls `x.contains(a)`
- Thinking `invoke` only works for lambdas, not user objects
- Confusing the membership `in` with the for-loop iteration `in`
- Claiming `contains` can return a non-Boolean