Статьи

Реализация нескольких интерфейсов через делегирование

В Kotlin класс может реализовывать несколько интерфейсов. Это общеизвестно. Класс также может использовать делегирование для реализации многочисленных интерфейсов, где реализации происходят из любых делегированных объектов, переданных в конструктор. Например:

01
02
03
04
05
06
07
08
09
10
11
class GeneticExperiment(human: Human, animal: Animal) : Human by human, Animal by animal
 
interface Human {
  fun eat()
  fun sleep()
  fun poop()
}
 
interface Animal {
  fun bite()
}

Этот странный класс наследует функциональность от объектов Human и Animal передаваемых в конструктор. Без делегирования любые функции на любом из интерфейсов должны быть записаны вручную внутри класса.

Важным моментом, который следует отметить, является тот факт, что Human и Animal не имеют общего родительского интерфейса.

Если у вас есть интерфейсы, выходящие из общего интерфейса, то будут возникать ошибки компиляции из-за конфликтующих реализаций делегированных объектов. Например, код:

01
02
03
04
05
06
07
08
09
10
11
12
class ShapeShifter(human: Human, weightLifter: WeightLifter) : Human by human, WeightLifter by weightLifter
 
interface Human {
  fun eat()
  fun sleep()
  fun poop()
}
 
interface WeightLifter : Human {
  fun liftHeavyStuff()
  fun pose()
}

Приводит к ошибкам:

01
02
03
04
05
06
07
08
09
10
11
12
Delegation.kt:80:1: error: class 'ShapeShifter' must override public open fun eat():
Unit defined in dev.lankydan.ShapeShifter because it inherits many implementations of it
class ShapeShifter(human: Human, weightLifter: WeightLifter) : Human by human, WeightLifter by weightLifter
^
Delegation.kt:80:1: error: class 'ShapeShifter' must override public open fun sleep():
Unit defined in dev.lankydan.ShapeShifter because it inherits many implementations of it
class ShapeShifter(human: Human, weightLifter: WeightLifter) : Human by human, WeightLifter by weightLifter
^
Delegation.kt:80:1: error: class 'ShapeShifter' must override public open fun poop():
Unit defined in dev.lankydan.ShapeShifter because it inherits many implementations of it
class ShapeShifter(human: Human, weightLifter: WeightLifter) : Human by human, WeightLifter by weightLifter
^

Как вы можете видеть из ошибок, класс ShapeShifter не знает, какую делегированную реализацию взять. Ошибки компиляции должны быть устранены путем явного переопределения конфликтующих функций. Переопределив функции, вы можете решить, какую делегированную реализацию вы хотите использовать, или вы можете предоставить новую. Исправленная версия этого класса может выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ShapeShifter(
  private val human: Human,
  private val weightLifter: WeightLifter
) : Human by human, WeightLifter by weightLifter {
   
  override fun eat() {
    human.eat()
  }
 
  override fun sleep() {
    weightLifter.sleep()
  }
 
  override fun poop() {
    println("?")
  }
}
 
interface Human {
  fun eat()
  fun sleep()
  fun poop()
}
 
interface WeightLifter : Human {
  fun liftHeavyStuff()
  fun pose()
}

Только функции, объявленные в интерфейсе Human должны быть переопределены вручную

Сделав небольшой шаг назад отсюда. Класс, который реализует дочерний интерфейс и делегирует его функциональность дочернему родительскому интерфейсу, будет компилироваться, пока все дочерние функции реализованы. Формулировка была сложной. Пример должен помочь уточнить, что я пытаюсь сказать:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class ShapeShifter(copied: Human) : WeightLifter, Human by copied {
 
  override fun liftHeavyStuff() {
    println("?️‍♂️")
  }
 
  override fun pose() {
    println("?")
  }
}
 
interface Human {
  fun eat()
  fun sleep()
  fun poop()
}
 
interface WeightLifter : Human {
  fun liftHeavyStuff()
  fun pose()
}

Только функции, объявленные в интерфейсе WeightLifter необходимо добавлять вручную

Чтобы подвести итог:

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

Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Реализация нескольких интерфейсов через делегирование

Мнения, высказанные участниками Java Code Geeks, являются их собственными.