Как делегировать реализацию собственности в Котлин?
Kotlin позволяет мне реализовать интерфейс, делегируя аргумент основного конструктора следующим образом:
class Foo(xs : ArrayList<Int>) : List<Int> by xs { }
Но это демонстрирует разработчику поддержки. Делегирование анонимного также выглядит нормально:
class Foo() : List<Int> by ArrayList<Int>() { }
Это скрывает детали реализации, но мы теряем доступ к функциям, не предоставленным интерфейсом, что в этом случае является изменчивостью.
Поэтому я хотел бы делегировать реализацию в свойство, которое не находится в основном конструкторе. То, что я хотел бы иметь, похоже на
class Foo() : List<Int> by xs {
val xs : List<Int> = ArrayList<Int>()
}
который не компилируется.
Можно ли явно определить свойство, определенное явным образом в классе класса, и все еще иметь возможность делегировать ему реализацию?
Ответы
Ответ 1
В настоящее время это невозможно. Выражение в by
-clause вычисляется только один раз перед построением класса, поэтому вы не можете ссылаться на символы этого класса.
В этом вопросе есть запрос в трекере, хотя это почти наверняка не будет поддерживаться в Kotlin 1.0.
Одна забавная обходная работа, которая иногда работает, заключается в том, чтобы сделать свойство, которое вы хотите быть делегатом, вместо параметра конструктора со значением по умолчанию. Таким образом, он будет доступен как в by
-clause, так и в теле класса:
class Foo(val xs: List<Int> = ArrayList<Int>()) : List<Int> by xs {
fun bar() {
println(xs)
}
}
Имейте в виду, что xs
в by xs
по-прежнему вычисляется только один раз здесь, поэтому даже если xs
является свойством var
, будет использоваться только значение по умолчанию, указанное в конструкторе. Это не универсальное решение, но иногда оно может помочь.
Ответ 2
Развернувшись на ответ Александра Удалова, я придумал решение с использованием частного базового класса
private open class FooBase(protected val xs : MutableList<Int>) : List<Int> by xs { }
class Foo() : FooBase(ArrayList()) {
fun bar() {
xs.add(5)
}
}
Теперь у меня есть доступ к свойству, поддерживающему мою реализацию интерфейса, но я не ограничусь операциями, предоставляемыми этим интерфейсом, все еще скрывая фактическую реализацию от пользователя.
Примечание.. Несмотря на то, что он работает, я получаю следующее предупреждение от IntelliJ IDEA 15 CE, которое возникает из проверки EXPOSED_SUPER_CLASS
: Устаревшее: эффективная видимость подкласса "public" должна быть такой же или менее разрешающей, чем эффективная видимость его суперкласса 'private'. Я не совсем уверен, что здесь означает устаревшая часть - будет ли предупреждение удалено в будущем или это не скомпилируется в какой-то момент. В любом случае нам не нужно использовать класс private open
, abstract
или просто open
, потому что, даже если пользователю разрешено создавать экземпляр FooBase
, он мало что может сделать с ним.
Обновление:
Существует актуальное простое и компактное решение, которое не использует никаких подозрительных действий:
class Foo private constructor(private val xs: ArrayList<Int>) : List<Int> by xs {
constructor() : this(ArrayList<Int>()) { }
}