Checkpoint: Functions & OOP

This checkpoint consolidates the whole unit — higher-order and inline functions, sequences, interfaces, objects, delegation, coroutines, and Flow — into one build challenge and a quiz.

Learn Checkpoint: Functions & OOP in our free Kotlin course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Work through the recap, complete the multi-step build challenge (a solution is provided), and test yourself with the checkpoint quiz before moving on to the capstone.

What This Checkpoint Covers

1️⃣ One-File Recap of the Whole Unit

Before the challenge, here is everything you've learned in a single runnable program: an interface with a default method, an object declaration singleton, an object expression , a higher-order function , a lazy sequence pipeline, and a delegated property . Read it top to bottom and predict each line of output.

If every line made sense, you're ready for the build challenge. If any surprised you, revisit that lesson before continuing.

2️⃣ Build Challenge: A Reporting Pipeline

Now put it together yourself. The challenge below asks you to combine an interface (with a default method), an object singleton that implements it, and a higher-order function running a lazy sequence pipeline. Try it first; the full, hand-verified solution is right below in a collapsible panel.

Here is one complete, correct implementation. The output is shown in the comment at the bottom.

3️⃣ Common Mistakes to Watch For

⏱ Timed Quiz

Answer each in your head, then expand to check. No peeking first!

It describes a function taking two Int s and returning an Int . If add holds such a value, you invoke it with add(2, 3) . A lambda {' '} or a reference ::plus can supply the value.

Generics are erased at runtime, so is T normally can't compile. Inlining copies the function body into each call site, where the concrete type is known and can be substituted — that substitution is exactly what reified relies on, so it's only allowed on inline functions.

Nothing — and the map never even runs. A sequence is lazy; without a terminal operation like toList() or forEach , no values are pulled through the pipeline.

An object declaration ( object Name {' '} ) is a single named singleton shared across the program. An object expression ( object : Type {' '} ) is an anonymous one-off object created fresh each time the line runs — Kotlin's anonymous class.

It defers computing the value until the first read, then caches the result so later reads reuse it without recomputing. A normal val is computed when the object is constructed.

launch is fire-and-forget and returns a Job ; async returns a Deferred<T> whose result you await() . A Flow is a cold, lazy stream of many values over time that runs only when collect ed and can suspend at each step.

Practice quiz

What does the function type (Int) -> Int describe?

  • A function taking an Int and returning an Int
  • An Int variable named Int
  • A range from Int to Int
  • A class with one Int field

Answer: A function taking an Int and returning an Int. (Int) -> Int is a function type: it takes one Int parameter and returns an Int.

Why must a function be marked inline to use a reified type parameter?

  • Reified is just a style choice
  • Inlining copies the body into each call site where the concrete type is known
  • Reified makes the function faster
  • Inline functions cannot have generics

Answer: Inlining copies the body into each call site where the concrete type is known. Generics are erased at runtime, but inlining substitutes the concrete type at each call site, which is what reified relies on.

What happens to listOf(1,2,3).asSequence().map { it * 2 } with no terminal operation?

Sequences are lazy; without a terminal operation like toList(), no element is ever pulled through the pipeline.

What is the difference between an object declaration and an object expression?

  • They are identical
  • A declaration is anonymous; an expression is named
  • A declaration is a named singleton; an expression is an anonymous one-off object
  • Only object expressions can implement interfaces

Answer: A declaration is a named singleton; an expression is an anonymous one-off object. object Name {} is a single named singleton, while object : Type {} creates a fresh anonymous object each time the line runs.

What does val x by lazy { ... } provide over a normal val?

  • It recomputes the value on every read
  • It defers computation until first read, then caches the result
  • It makes the value mutable
  • It runs the value on a background thread

Answer: It defers computation until first read, then caches the result. lazy delays the computation until the first access and caches the result for later reads.

In coroutines, what does launch return?

  • A Deferred<T>
  • A Job
  • The computed result directly
  • A Flow

Answer: A Job. launch is fire-and-forget and returns a Job; async returns a Deferred<T> whose result you await().

What is a Flow in Kotlin?

  • A single suspending value
  • A cold, lazy stream of values that runs only when collected
  • A thread pool
  • A synchronous list

Answer: A cold, lazy stream of values that runs only when collected. A Flow is a cold, lazy asynchronous stream that emits values only when collected.

Which keyword forwards an interface implementation to a wrapped object without boilerplate?

  • by
  • with
  • delegate
  • forward

Answer: by. Class delegation uses by, as in class C(impl: I) : I by impl, forwarding all members to impl.

Why does a higher-order function need a function type parameter like op: (Int) -> Int?

  • To accept another function as an argument
  • To return an Int constant
  • To declare a generic class
  • To mark the function inline

Answer: To accept another function as an argument. A higher-order function takes or returns functions; the function-type parameter lets you pass behavior in, e.g. values.map(op).

When does a sequence pipeline actually execute its filter and map steps?

  • Immediately when defined
  • When a terminal operation like toList() or sum() is called
  • Only inside a coroutine
  • When the program exits

Answer: When a terminal operation like toList() or sum() is called. Sequence intermediate operations are lazy; the chain runs only when a terminal operation pulls the values through.