Named Interface Delegation

Motivation

Kotlin’s interface delegation provides a convenient way to forward interface implementations:

class MyService(
    name: String
) : Service by DefaultService(name)

However, the delegate instance is not directly accessible from the declaring class’s source code unless an additional property is explicitly declared.

This becomes inconvenient when implementing more advanced object composition patterns.

For example, I may want to:

  • Access the original delegate instance.

  • Wrap an existing delegate with another implementation.

  • Expose the delegate through a different API.

  • Reuse the same object for multiple delegated interfaces.

  • Build additional functionality on top of the delegate.

Currently, these use cases often require introducing an additional property just to retain a reference to an object that the compiler already manages internally.

Proposed Syntax

I propose introducing optional named interface delegation:

interface AbilityA {
    fun doA()
}

interface AbilityB {
    fun doB()
}

class DelegateClass<T>(
    foo: Int
) : AbilityA by DefaultAbilityA(),
    AbilityB@abilityB by DefaultAbilityB(foo) {

    val delegate get() = this@abilityB
}

Here, @abilityB assigns a name to the delegate instance, and this@abilityB accesses that instance.

The name identifies the resulting delegate object rather than exposing its generated JVM field.

A named delegate can also be reused when implementing multiple interfaces:

class MyService :
    AbilityA@shared by DefaultAbility(),
    AbilityB by this@shared {

    val original get() = this@shared
}

In this example:

  • AbilityA@shared declares a named delegate.

  • AbilityB by this@shared reuses the previously declared delegate instance.

  • Both interfaces are implemented using the same object.

Declaring another named delegate with the same label should be a compilation error.

Why Use @?

Kotlin already uses labels with @, including labeled this expressions:

this@Outer

Using the same mechanism for named delegates seems reasonably consistent with existing Kotlin syntax.

I envision named delegates as class-level references, conceptually similar to class labels such as Outer in this@Outer.

They should not be treated as ordinary properties or local variables. Instead, they provide a source-level way to reference delegate instances introduced through interface delegation.

This distinction is important because the purpose is to expose the delegate instance without requiring an additional property declaration.

Proposed Semantics

I imagine the following rules:

  1. Naming a delegate is optional. Existing interface delegation syntax remains unchanged.

  2. A named delegate introduces a class-level reference to the resulting delegate instance.

  3. A named delegate can be accessed using this@label within the declaring class, following the general model of Kotlin’s labeled this expressions.

  4. Each named delegate must have a unique label within its declaring class. Declaring multiple named delegates with the same label is a compilation error.

  5. Other delegated interfaces may reuse an existing named delegate instance through this@label.

  6. A named delegate refers to the actual delegate instance, not a compiler-generated JVM field or a Kotlin property.

  7. A named delegate does not introduce an ordinary property or getter. It is a source-level reference provided by the language.

  8. The delegate expression is evaluated exactly once, preserving existing initialization semantics.

  9. The feature should be independent of the JVM backend, if possible.

Example: Delegate Composition

One use case is building additional functionality around an existing delegate:

class Wrapper(foo: Int) :
    AbilityB@abilityB by DefaultAbilityB(foo) {

    val enhanced by lazy {
        EnhancedAbility(this@abilityB)
    }
}

Without named delegate access, I would need to declare an additional property:

class Wrapper(foo: Int) {
    private val abilityB = DefaultAbilityB(foo)

    // Explicitly forward the interface implementation.
}

This introduces an additional property solely to retain a reference to the delegate instance.

Named interface delegation would make this object composition more direct.

Questions

  • Would exposing named delegate instances be compatible with the current delegation model?

  • Could this@label be used as a class-level reference, similarly to existing labeled this expressions?

  • Should named delegates declared in a superclass also be accessible from subclasses?

  • Would treating named delegates as source-level references rather than ordinary properties introduce any implementation difficulties?

  • Are there initialization or overload-resolution concerns that make this syntax problematic?

  • Is there already an existing proposal addressing this use case?

I would appreciate feedback on both the syntax and the underlying design.

The main goal is to make interface delegation more useful for object composition without changing its existing behavior or requiring additional properties just to retain references to delegate instances.

FWIW, this works as a workaround:

interface AbilityA {
    fun doA()
}

interface AbilityB {
    fun doB()
}

class DelegateClass<T> private constructor(
    val delegate: DefaultAbilityB,
) : AbilityA by DefaultAbilityA(),
    AbilityB@abilityB by delegate {
    constructor(foo: Int): this(DefaultAbilityB(foo))
}

I guess I don’t really see the point of this. Or rather, I should say, it seems like such a minor improvement. Is it really that bad to assign a delegate to a field so that you can reference it elsewhere? It’s saving 1 line of code. Hardly seems like it’ll overcome the -100 language feature.

Also is your picture Shiroko from Blue Archive? Nice.

1 Like

I think it’s more than just saving a line of code.

Sometimes I want to compose interfaces using internally constructed implementations without allowing callers to replace them through constructor parameters.

The usual workaround is to use a private primary constructor and a public secondary constructor(or companion object), which works, but gets rather awkward when there are multiple delegates.

I’d also like to access the delegate instances themselves for further composition, without having to declare separate properties just to keep references to them.

So it’s not really about saving a line of code, but making interface delegation more flexible while keeping the implementation details encapsulated.

Edit: Yes, I like Shiroko