How do I use the this, super, and @ labels for disambiguation in Kotlin class hierarchies?

In Kotlin, this, super, and @ labels are used to disambiguate which receiver or superclass member you mean, especially in nested scopes, inheritance, and inner classes.

1. this: refer to the current receiver

Inside a class, this refers to the current instance of that class.

class User(val name: String) {
    fun printName() {
        println(this.name)
    }
}

Usually this is optional:

println(name)

is the same as:

println(this.name)

2. this@Label: choose a specific outer receiver

When you have nested classes, lambdas, or extension functions, there may be multiple possible this receivers. Kotlin lets you qualify this with a label.

Class receiver

class Outer {
    val name = "Outer"

    inner class Inner {
        val name = "Inner"

        fun printNames() {
            println(this.name)        // Inner
            println([email protected])  // Inner
            println([email protected])  // Outer
        }
    }
}

this@Outer explicitly means “the this of Outer”.


3. Labels in lambdas

You can label lambdas and then use this@label to access that lambda’s receiver.

class Html {
    fun body() {
        println("body")
    }
}

fun html(block: Html.() -> Unit) {
    Html().block()
}

fun main() {
    html outer@ {
        this.body()
        [email protected]()
    }
}

Here:

this@outer

refers to the receiver of the lambda labeled outer.


4. super: call superclass implementation

Use super to access a member from the immediate superclass.

open class Parent {
    open fun greet() {
        println("Hello from Parent")
    }
}

class Child : Parent() {
    override fun greet() {
        super.greet()
        println("Hello from Child")
    }
}

Output:

Hello from Parent
Hello from Child

5. super<Type>: disambiguate multiple inherited implementations

If a class inherits the same member from multiple supertypes, you must specify which one to call.

interface A {
    fun greet() {
        println("Hello from A")
    }
}

interface B {
    fun greet() {
        println("Hello from B")
    }
}

class C : A, B {
    override fun greet() {
        super<A>.greet()
        super<B>.greet()
        println("Hello from C")
    }
}

Here:

super<A>.greet()
super<B>.greet()

select the specific supertype implementation.


6. super@Label: access an outer class’s superclass

In inner classes, super normally refers to the superclass of the inner class. If you need the superclass of an outer class, use a qualified super.

open class Base {
    open fun message() {
        println("Base")
    }
}

open class OuterBase : Base() {
    override fun message() {
        println("OuterBase")
    }
}

class Outer : OuterBase() {
    override fun message() {
        println("Outer")
    }

    inner class Inner {
        fun callOuterSuper() {
            [email protected]()
        }
    }
}

Here:

[email protected]()

means “call the superclass implementation of Outer”.

So this calls:

OuterBase.message()

not Outer.message().


7. Combining super<Type>@Label

If the outer class implements multiple supertypes, you can combine both forms.

interface A {
    fun print() {
        println("A")
    }
}

interface B {
    fun print() {
        println("B")
    }
}

class Outer : A, B {
    override fun print() {
        println("Outer")
    }

    inner class Inner {
        fun callOuterSupers() {
            super<A>@Outer.print()
            super<B>@Outer.print()
        }
    }
}

Here:

super<A>@Outer.print()
super<B>@Outer.print()

means:

  • call A’s implementation as inherited by Outer
  • call B’s implementation as inherited by Outer

Summary

Syntax Meaning
this Current receiver
this@Outer this of a specific labeled or outer receiver
super Immediate superclass implementation
super<Type> Specific superclass or interface implementation
super@Outer Superclass implementation of an outer class
super<Type>@Outer Specific supertype implementation of an outer class

In short:

this@Something

chooses which object/receiver you mean.

super<Something>

chooses which superclass/interface implementation you mean.

super<Something>@Outer

chooses which supertype implementation of which outer receiver you mean.

How do I return early using return, break, and continue in Kotlin loops?

In Kotlin, you can use return, break, and continue inside loops to control the flow. Here’s a detailed explanation and examples for each:

1. Using return

The return statement is used to terminate the execution of an enclosing function or lambda expression, and optionally return a value from it.
If used inside a loop, it will exit the entire function, not just the loop.

Example:

fun findNumber(numbers: List<Int>, target: Int): Boolean {
    for (number in numbers) {
        if (number == target) {
            return true // Terminates the function and returns true
        }
    }
    return false // If no match is found, this is executed after the loop
}

2. Using break

The break statement is used to exit the current loop (for, while, or do-while). The loop terminates immediately, and execution continues with the statement after the loop.

Example:

fun printUntilFive(numbers: List<Int>) {
    for (number in numbers) {
        if (number == 5) {
            break // Exit the loop when number is 5
        }
        println(number)
    }
    println("Loop ended.")
}

Output (for printUntilFive(listOf(1, 2, 3, 5, 6))):

1
2
3
Loop ended.

3. Using continue

The continue statement skips the current iteration of the loop, jumping to the next iteration immediately. It does not terminate the loop.

Example:

fun skipOddNumbers(numbers: List<Int>) {
    for (number in numbers) {
        if (number % 2 != 0) {
            continue // Skip the current iteration for odd numbers
        }
        println(number) // This will only print even numbers
    }
}

Output (for skipOddNumbers(listOf(1, 2, 3, 4, 5, 6))):

2
4
6

Using return, break, and continue in Nested Loops

In Kotlin, when dealing with nested loops, these keywords operate on the innermost enclosing loop by default. To modify this behavior, you can use labels.

Example with Labels:

fun labeledExample() {
    outer@ for (i in 1..3) {
        for (j in 1..3) {
            if (i == 2 && j == 2) {
                break@outer // Exits the outer loop entirely
            }
            println("i = $i, j = $j")
        }
    }
}

Output:

i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1

Summary

  • return: Exits the entire function or lambda.
  • break: Exits the current loop.
  • continue: Skips the current iteration and moves to the next.
  • Labels (e.g., @outer): Allow you to terminate or skip specific labeled loops in nested cases.

These keywords provide a powerful way to control Kotlin loop flows efficiently!