Why only interfaces can be delegated to in kotlin?
Asked Answered
W

1

24

I have seen few similar questions, but none had explained why delegation is limited to interfaces?

Most of the time in practice we have something that has actually no interface at all, it is a class that implements nothing but provides some functionality or implements an abstract class.

Is there any fundamental limitation that forces this to be limited to interfaces or can we expect kotlin to have unrestricted delegation in the future?

This is especially useful if we want to extend functionality of a class using composition not inheritance.

class A {}
class B(val a: A) : A by a {}
Withy answered 24/9, 2017 at 7:8 Comment(0)
S
21

When you delegate an interface, the class does still implement the interface. So for consistency, if you can delegate a class, it should work the same way. I.e.

class A(x: Int) {
  fun foo() = x
}

class B(val a: A) : A by a {}

needs to compile to

class B(val a: A) : A {
  override fun foo() = a.foo()
}

except this doesn't work:

  1. foo isn't open and can't be overridden.

  2. you need to call a constructor of A. class B(val a: A) : A(a.x) won't help either: x is not a member of A.

  3. What about equals and hashCode: are they delegated? Either decision would lead to weird consequences.

Searchlight answered 24/9, 2017 at 7:41 Comment(6)
1. So it shouldn't try to override it. Only overridable methods (open or abstact) should be delegated automatically. 2. So B should be required to pass the constructor parameters up to A as usual. 3. The Kotlin authors already had to make this decision for interface delegation since every interface implicitly extends Any and thus has equals, hashCode and toString.Lister
Sure, it's a possible set of answers. But then you don't "extend functionality of a class using composition not inheritance"; you have a pretty weird mix of composition and inheritance, and making a method in A open without changing its behavior (e.g. because you figured out C needs to override it) can change B's behavior.Searchlight
if A is passed to B as a constructor parameter a, how and why would you call the constructor of A?Gaul
Why: because to extend A, your primary constructor always has to call a constructor of A. How: well, that's exactly the problem.Searchlight
@AlexeyRomanov But A's constructor has already been called. It seems that in this case we could forgo calling super when initializing B. I understand that this might not be possible in JVM, well, at least, not in Java, but perhaps feasible in Kotlin?Gaul
"It seems that in this case we could forgo calling super when initializing B" That could break invariants implementation of A relies on (for example, in any methods you override and don't just call a's version).Searchlight

© 2022 - 2024 — McMap. All rights reserved.