Intereting Posts
Мне просто не кажется, что мой Kotlin Recycler Multiple View Code разобрался Использование Kotlin и Firebase для запуска sendEmailVerification с обратным вызовом onAuthStateChanged Сценарий колыбели Котлин и зависимость Как поместить диалог в нижнюю часть Java / Kotlin шифрует ключ AES с открытым и открытым ключом Сравнение двух вариантов в Котлине заполнять прядильщик с модифицированным и моши Ошибка виртуального метода при использовании Parceler в Kotlin Android Использование данных Amazon AWS Cognito `. Well-known / jwks.json` не позволяет base64 декодировать некоторые поля Перехват метода перегрузки, полностью переопределяющий метод Как получить страну пользователя для HTTP-запроса в бэкэнд? KotlinJS: Когда я буду использовать динамический тип Kotlin Воспроизведение звуков из сырого файла в Котлине Реализация Java-аннотации в Котлине java.lang.Integer нельзя передать в java.lang.Long в Kotlin (когда начальное значение равно null)

Как делегировать реализацию собственности в Котлин?

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>() } 

который не компилируется.

Возможно ли, что свойство явно определено в классе и все еще может делегировать ему реализацию?

В настоящее время это невозможно. Выражение in -clause вычисляется только один раз перед построением класса, поэтому вы не можете ссылаться на символы этого класса.

В этом вопросе есть запрос в трекер-проблеме , хотя в Kotlin 1.0 он почти не поддерживается.

Один из забавных решений, которые иногда работают, заключается в том, чтобы сделать свойство, которым вы хотите быть делегатом, вместо параметра конструктора со значением по умолчанию. Таким образом, он будет доступен как в классах by -clause, так и в классе:

 class Foo(val xs: List<Int> = ArrayList<Int>()) : List<Int> by xs { fun bar() { println(xs) } } 

Имейте в виду, что xs in by xs все еще вычисляется только один раз здесь, поэтому, даже если xs является свойством var , будет использоваться только значение по умолчанию, указанное в конструкторе. Это не универсальное решение, но иногда оно может помочь.

Расширяясь по ответам Александра Удалова, я придумал решение с использованием частного базового класса

 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 , он не может с ним FooBase .

Обновить:

Существует фактически простое и компактное решение, которое не использует никаких подозрительных действий:

 class Foo private constructor(private val xs: ArrayList<Int>) : List<Int> by xs { constructor() : this(ArrayList<Int>()) { } }