На этом шаге мы рассмотрим особенности этой реализации.
Под использованием интерфейса мы имеем в виду "реализацию" его в классе. Для этого нужно, во-первых, объявить, что класс реализует интерфейс, и, во-вторых, добавить реализацию всех свойств и функций, указанных в интерфейсе.
Используйте оператор :, чтобы объявить, что класс Player реализует интерфейс Fightable.
. . . . class Player (_name: String, override var healthPoints: Int = 100, val isBlessed: Boolean, private val isImmortal: Boolean) : Fightable { . . . . }
Рис.1. Реализация интерфейса (Player.kt)
Когда вы добавляете интерфейс Fightable в Player, IntelliJ сообщает об отсутствии свойств и функций. Предупреждение об отсутствии реализации функций и свойств в Player поможет вам соблюсти правила Fightable, а IntelliJ поможет реализовать все необходимое.
Щелкните правой кнопкой мыши на Player и выберите Generate... | Implement Methods... (рисунок 2) и затем в диалоговом окне Implement Members (рисунок 3) выберите direCount, diceSides и attack(). (О damageRoll поговорим в следующих шагах.)
Рис.2. Выбор Generate... | Implement Methods...
Рис.3. Реализация членов Fightable
Вы увидите, как был добавлен следующий код в класс Player:
Рис.4. Добавленный код
Реализации функций, добавленных в Player, пока представлены простыми заглушками. Вам нужно заменить их настоящими реализациями. (Вы могли заметить функцию TODO. Тут она показана в действии, или, скорее, в состоянии ожидания.) Как только вы реализуете эти свойства и функции, Player будет удовлетворять интерфейсу Fightable и сможет участвовать в сражениях.
Обратите внимание, что во всех реализациях свойств и функций используется ключевое слово override. Это может стать неожиданностью: все-таки вы не заменяете реализацию этих свойств в Fightable. Тем не менее все реализации свойств и функций интерфейса должны быть отмечены словом override.
С другой стороны, ключевое слово open не нужно при объявлении функций в интерфейсе. Это связано с тем, что все свойства и функции, добавленные в интерфейс, должны, несомненно, иметь модификатор доступа open, иначе их реализация не будет иметь никакого смысла. В конце концов, интерфейс определяет, что нужно реализовать, а как - это уже ответственность классов, реализующих его.
Замените вызовы TODO в direCount, diceSides и attack() на необходимые значения и функции.
. . . . class Player (_name: String, override var healthPoints: Int = 100, val isBlessed: Boolean, private val isImmortal: Boolean) : Fightable { override val diceCount: kotlin.Int = 3 override val diceSides: kotlin.Int = 6 override fun attack(opponent: com.bignerdranch.nyethack.Fightable): kotlin.Int { val damageDealt = if (isBlessed) { damageRoll * 2 } else { damageRoll } opponent.healthPoints -= damageDealt return damageDealt } . . . . }
Параметры direCount и diceSides инициализированы целыми числами. Функция attack() извлекает значение свойства damageRoll (которое пока не конкретизировано) и удваивает его, если игрок благословлен. Затем она вычитает результат из свойства healthPoints экземпляра opponent, наличие которого гарантируется независимо от его класса, потому что класс реализует Fightable. В этом заключается красота интерфейса.
На следующем шаге мы рассмотрим реализацию по умолчанию.