Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 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>()) { }
}