Вызов (базовая) делегированная функция при использовании делегирования класса из переопределения
При переопределении метода интерфейса, реализуемого делегированием класса, возможно ли вызвать класс, который обычно делегируется из переопределяющей функции? Подобно тому, как вы бы назвали super
при использовании наследования.
Из документации:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}
Обратите внимание, что переопределения работают так, как вы могли ожидать: компилятор будет использовать ваши реализации переопределения вместо тех, что в объекте делегата.
override fun print() { ... }
Как позвонить BaseImpl
print()
функция из этой переопределенной функции?
Вариант использования: я хочу добавить дополнительную логику к этой функции, одновременно используя существующую реализацию.
6 ответов
@ Ответ Егора работает и должен быть подходящим, когда делегированный класс передается как параметр (внедрение зависимости).
Однако в моем более конкретном случае использования (извините за то, что я не прояснил это в моем вопросе) реализация напрямую определяется в определении делегирования.
class Derived(b: Base) : Base by BaseImpl()
В этом случае ответ Егора не работает, и необходим более сложный подход. Следующая реализация может использоваться, если вы хотите сохранить реализацию скрытой, чтобы вызывающие не могли ее изменить. Например, в моем конкретном случае использования я создаю совокупный корень и хочу защитить внутренние инварианты.
open class HiddenBaseImpl internal constructor( protected val _b: Base ) : Base by _b
class Derived() : HiddenBaseImpl( BaseImpl() )
{
override fun print()
{
_b.print()
... do something extra
}
}
поскольку HiddenBaseImpl
Первичный конструктор может использоваться только внутри, вызывающие библиотеки не могут создать экземпляр этого класса и поэтому вынуждены использовать Derived
, Derived
теперь можно вызывать класс, делегированный для внутреннего использования, и добавлять дополнительное поведение, не позволяя вызывающим сторонам передавать различные реализации Base
,
Поскольку Base
это интерфейс, вы не можете позвонить super
на нем (похоже на Java).
Вместо этого вам нужно объявить b
как поле и использовать его напрямую:
class Derived(val b: Base) : Base by b {
override fun print() {
b.print()
// ...
}
}
Вы можете использовать только super
в Kotlin для доступа к суперклассу, без интерфейсов или других странных вещей (помните - Kotlin работает на JVM). Тем не менее, это совершенно нормально, если вы храните производный экземпляр в переменной, как предложил Егор в своем ответе.
Чтобы никто не мог установить или получить переменную, вы можете использовать закрытый (или защищенный, в зависимости от вашего случая) первичный конструктор и добавить второй общедоступный конструктор:
interface Base {
fun print()
}
class BaseImpl() : Base {
override fun print() { print(x) }
}
class Derived private constructor(private val b : Base) : Base by b {
constructor() : this(BaseImpl())
override fun print() = b.print()
}
fun main(args: Array<String>) {
val d = Derived()
d.b // does not work, compiler error
d.print() // prints 10
}
Я просто хочу добавить решение для этого случая при использовании внедрения зависимостей.
Вы можете использовать вторичный конструктор для получения ваших инъекций, а затем использовать первичный конструктор для создания вашего делегированного объекта.
class BaseImpl(injectedObject: Object)
class Derived(
private val baseImpl: BaseImpl,
): Base by baseImpl {
// Used to construct your object by Dagger or any other DI Framework
@Inject constructor(injectedObject: Object): this(BaseImpl(injectedObject))
override fun print() {
baseImpl.print()
// Do your own thing
}
}
Думаю, у меня есть лучшее решение для этого. Он более элегантен в том смысле, что не требует дополнительного класса и выполняет те же функции, что и утвержденный.
interface Base {
fun print()
}
class Derived private constructor (private val delegate: Base): Base by delegate {
constructor(): this(BaseImpl())
override fun print{
delegate.print()
}
}
Вы можете вызвать непосредственно функцию print()
из собственности b
, но для этого вам нужно перейти от простого параметра конструктора b: Base
сохранить параметр как свойство private val b: Base
Надеюсь, что это поможет вам.