21

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 {}
vach
  • 10,571
  • 12
  • 68
  • 106

1 Answers1

18

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.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • 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`. – Jesse Dec 21 '17 at 15:02
  • 1
    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. – Alexey Romanov Dec 21 '17 at 15:53
  • if `A` is passed to `B` as a constructor parameter `a`, how and *why* would you call the constructor of `A`? – squirrel Oct 22 '20 at 09:48
  • Why: because to extend `A`, your primary constructor _always_ has to call a constructor of `A`. How: well, that's exactly the problem. – Alexey Romanov Oct 22 '20 at 11:22