На этом шаге мы рассмотрим назначение и реализацию перегрузки.
Вы уже знаете, что встроенные типы языка Kotlin поддерживают множество операций и некоторые даже адаптируют эти операции в зависимости от представляемых ими данных. Например, функция equals() и связанный с ней оператор ==. С их помощью можно проверить, равны ли два экземпляра числового типа, содержат ли строки одинаковый набор символов и являются ли значения свойств двух экземпляров класса данных, объявленных в главном конструкторе, эквивалентными. Аналогично функция plus() и оператор + складывают два числа, добавляют одну строку в конец другой и добавляют элементы из одного списка в другой.
При работе с пользовательскими типами компилятор Kotlin не всегда знает, как к ним применить встроенные операторы. Как, например, сравнить два экземпляра Player? Если вы хотите использовать встроенные операторы со своими типами, переопределите функции-операторы, чтобы подсказать компилятору, что он должен делать с вашими типами. Этот процесс называется перегрузкой операторов.
Coordinate - явный кандидат на улучшение через перегрузку операторов. Вы перемещаете героя по миру, складывая свойства двух экземпляров Coordinate вместе. Вместо того чтобы описывать эту работу в Direction, можно перегрузить оператор plus для Coordinate.
Сделаем так в Navigator.kt: добавьте функцию с модификатором operator.
enum class Direction(private val coordinate: Coordinate) { NORTH(Coordinate(0, -1)), EAST(Coordinate(1, 0)), SOUTH(Coordinate(0, 1)), WEST(Coordinate(-1, 0)) fun updateCoordinate(playerCoordinate: Coordinate) = Coordinate(playerCoordinate.x + coordinate.x, playerCoordinate.y + coordinate.y) } data class Coordinate(val x: Int, val y: Int) { val isInBounds = x >= 0 && y >= 0 operator fun plus(other: Coordinate) = Coordinate(x + other.x, y + other.y) }
Рис.1. Перегрузка оператора plus (Navigator.kt)
Теперь просто используйте оператор сложения (+), чтобы прибавить один экземпляр Coordinate к другому. Давайте сделаем это в Direction.
enum class Direction(private val coordinate: Coordinate) { NORTH(Coordinate(0, -1)), EAST(Coordinate(1, 0)), SOUTH(Coordinate(0, 1)), WEST(Coordinate(-1, 0)) fun updateCoordinate(playerCoordinate: Coordinate) = coordinate + playerCoordinate } data class Coordinate(val x: Int, val y: Int) { val isInBounds = x >= 0 && y >= 0 operator fun plus(other: Coordinate) = Coordinate(x + other.x, y + other.y) }
Рис.2. Использование перегруженного оператора (Navigator.kt)
В таблице 1 перечислены часто применяемые операторы, которые могут быть перегружены.
Оператор | Имя функции | Назначение |
---|---|---|
+ | plus() | Складывает два объекта |
+= | plusAssign() | Складывает два объекта и присваивает сумму первому |
== | equals() | Возвращает true, если два объекта эквивалентны, и false, если нет |
> | compareTo() | Возвращает true, если объект слева от оператора имеет большее значение, чем объект справа, иначе возвращает false |
[] | get() | Возвращает элемент из коллекции по индексу |
.. | rangeTo() | Создает интервал |
in | contains() | Возвращает true, если объект присутствует в коллекции |
Эти операторы могут быть перегружены в любом классе, но делать это стоит только тогда, когда в этом есть смысл. Собираясь изменить логику оператора сложения в классе Player, задайте себе сначала вопрос: "Что подразумевается под понятием "игрок плюс игрок"? Попробуйте ответить на него, прежде чем перегружать оператор.
Кстати, если вы решите переопределить equals(), также стоит переопределить функцию hashCode(). Пример переопределения этих функций через специальную команду IntelliJ, будет показан на шаге, посвященном алгебраическим типам данных. Подробное объяснение, почему надо переопределять hashCode(), выходит за рамки нашего изложения. Если вам интересен этот вопрос, перейдите по ссылке https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/hash-code.html.
На следующем шаге мы займемся исследованием мира NyetHack.