Почему в котлине можно делегировать только интерфейсы?

Я видел несколько похожих вопросов, но ни один не объяснил, почему делегирование ограничено интерфейсами?

В большинстве случаев на практике у нас есть что-то, что на самом деле вообще не имеет интерфейса, это класс, который ничего не реализует, но предоставляет некоторые функции или реализует абстрактный класс.

Есть ли какое-то фундаментальное ограничение, которое заставляет это быть ограниченным интерфейсами, или мы можем ожидать, что kotlin будет иметь неограниченное делегирование в будущем?

Это особенно полезно, если мы хотим расширить функциональность класса, используя композицию, а не наследование.

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

person vach    schedule 24.09.2017    source источник


Ответы (1)


Когда вы делегируете интерфейс, класс по-прежнему реализует интерфейс. Так что для единообразия, если вы можете делегировать класс, он должен работать так же. Т.е.

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

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

необходимо скомпилировать в

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

за исключением того, что это не работает:

  1. foo не open и не может быть изменен.

  2. вам нужно вызвать конструктор A. class B(val a: A) : A(a.x) тоже не поможет: x не является участником A.

  3. А как насчет equals и hashCode: они делегированы? Любое решение привело бы к странным последствиям.

person Alexey Romanov    schedule 24.09.2017
comment
1. Таким образом, он не должен пытаться отменить это. Только переопределяемые методы (open или abstact) должны делегироваться автоматически. 2. Таким образом, B должно быть обязано передавать параметры конструктора до A, как обычно. 3. Авторам Kotlin уже пришлось принять это решение для делегирования интерфейса, поскольку каждый интерфейс неявно расширяет Any и, таким образом, имеет equals, hashCode и toString. - person Jesse; 21.12.2017
comment
Конечно, это возможный набор ответов. Но тогда вы не расширяете функциональность класса, используя композицию, а не наследование; у вас довольно странное сочетание композиции и наследования, и открытие метода в A без изменения его поведения (например, потому что вы выяснили, что C необходимо его переопределить) может изменить поведение B. - person Alexey Romanov; 21.12.2017
comment
если A передается в B в качестве параметра конструктора a, как и почему вы должны вызывать конструктор A? - person squirrel; 22.10.2020
comment
Почему: потому что для расширения A ваш основной конструктор всегда должен вызывать конструктор A. Как: ну вот и проблема. - person Alexey Romanov; 22.10.2020