Урок 19: ENUM в Kotlin (классы перечислений), TODO() и тип Nothing

ENUM классы перечислений TODO и тип Nothing - Android [Kotlin] для начинающих

Что такое enum?

Разбираем классы перечислений enum и выясняем чем они полезнее обычных строковых констант. Также затронем функцию TODO(), которая возвращает тип “ничего”.

enum – это сокращение от enumeration. Перечисления с помощью enum классов используются, когда нужно перечислить какие-либо объекты. Такими объектами как правило являются константы, их значение нам известно заранее. enum полезен там, где сущность может принимать одно из заранее известных вариантов значений. Чтобы потом обращаться к ним по названию этого класса, чтобы использовать далее. enum упрощает код, ограничивая количество возможных состояний.

Понятие — статус

Хранить можно как просто какой-то объект, так и объект с присвоенным ему конкретным свойством. Это могут быть дни недели. Или цвета, когда нам нужно сопоставлять название цвета и его (и только его) HEX-код. Это может быть хранение статусов какого-нибудь состояния или процесса. Например, enum класс хранит название статуса готовки блюда в приложении с доставкой еды и идентификатор. Когда сервер присылает заранее известный id статуса, мы в приложении сопоставляем его с имеющимися данными. И, определив актуальный статус, передаем его в методы по отрисовке данных процесса приготовления. Где уже определяется какую информацию для какого статуса отображать пользователю.

Создание класса перечислений

Посмотрим, как это выглядит в коде. Чтобы написать класс-перечисление нужно указать при создании enum class. Затем название класса с заглавной буквы, как и классический класс. В теле перечисляем наши значения через запятую, пишутся они как и другие константы стилем uppercase. Статусы пусть будут такими: NEW, COOKING, COMPLETED.

enum class Status {
	NEW,
	COOKING,
	COMPLETED,
}

Далее нам нужен механизм, имитирующий отправку какого нибудь кода статуса готовки с сервера. Теоретически сервер может отправлять такие названия строками и мы бы сравнивали их с названиями статусов. Но предположим, что сервер вместо строковых констант кодирует статусы целыми числами и отправляет их. Это будет целочисленный список statusesFromServer. Пусть они будут такими для наших трех статусов соответственно: 101, 102, 103.

Далее организуем имитацию того, что новый код статуса будет присылаться каждую секунду. Понадобится цикл for с итерациями по списку статусов и метод sleep(). Выведем коды пока что в консоль в исходном виде. Отлично, подготовка завершена.

fun main() {

val statusesFromServer = listOf(101,102,103)

for (i in statusesFromServer) {
			println(statusesFromServer(i))
      Thread.sleep(1000)
   }
}

enum class Status {
		NEW,
		COOKING,
		COMPLETED,
}

Создание функции для поиска статусов

Теперь вспоминаем, что нам нужно сделать. При получении определенного статуса, нужно найти его в enum классе и запустить последующую его обработку. Подготовим специальную функцию для этого setStatus(). Она будет принимать тип класса-перечисления. А внутри с помощью when печатать соответствующее статусу сообщение. Чтобы выбрать какой-то объект обращаемся к нему по имени класса. Или с помощью контекстного меню добавить импорт этого класса в файл, тогда можно будет просто писать название объекта. Но обратите внимание, среда разработки сама может предложить дозаполнить when. И это еще одно преимущество использования enum в виде его качества ограничивать возможные состояния приложения. Про TODO() расскажу в последней части урока. И я бы ввел еще один статус – ERROR. Будем обрабатывать ошибку на случай, если что-то пойдет не так.

fun setStatus(status: Status) {
   when (status) {
      Status.NEW -> println("Заказ принят")
      Status.COOKING -> println("Заказ готовится")
      Status.COMPLETED -> println("Заказ готов")
      Status.ERROR -> println("Статус неизвестен")
   }
}

Осталось только в методе main() парсить id статуса с гипотетического сервера и отправлять в эту функцию соответствующий объект. Убираем распечатку кода и снова воспользуемся when. Сопоставлять будем соответственно код и вызов функции вывода статуса из enum класса. ERROR положим в ветвление else. Ошибка будет отправляться, если придет код отличный от заранее известных.

for (i in statusesFromServer) {
   when (i) {
      101 -> setStatus(Status.NEW)
      102 -> setStatus(Status.COOKING)
      103 -> setStatus(Status.COMPLETED)
      else -> setStatus(Status.ERROR)
   }
   Thread.sleep(1000)
}

Что можно хранить в enum?

Вроде бы все работает неплохо, но голые коды статусов выглядят не очень. И мы можем хранить их прямо в enum. Мы можем хранить какие угодно свойства в enum. Для этого у класса добавляем конструктор с объявлением целочисленного свойства id. И для перечислений в скобках появляется необходимость заполнить это свойство, там и пропишем коды статусов. Для ERROR просто проставим 0. Или можно сделать свойство в id в конструкторе нуллабельным со значением по умолчанию null. В этом случае инициализировать свойство для ERROR не придется. Таким образом в main() вместо целочисленных значений выводим свойства этих объектов. Запускаем и наслаждаемся.

fun main() {

for (i in statusesFromServer) {
    when (i) {
       Status.NEW.id -> setStatus(Status.NEW)
	   Status.COOKING.id -> setStatus(Status.COOKING)
       Status.COMPLETED.id -> setStatus(Status.COMPLETED)
       else -> setStatus(Status.ERROR)
    }
    Thread.sleep(1000)
   }
}

enum class Status(val id: Int) {
		NEW(101),
		COOKING(102),
		COMPLETED(103),
		ERROR(0),
}

fun setStatus(status: Status) {
   when (status) {
      Status.NEW -> println("Заказ принят")
      Status.COOKING -> println("Заказ готовится")
      Status.COMPLETED -> println("Заказ готов")
      Status.ERROR -> println("Статус неизвестен")
   }
}

Окей. У объектов enum читать можно не только свойства. У класса есть поле name, возвращающее имя задекларированной константы. Еще при необходимости можно получить порядковый номер константы из поля ordinal. И, наконец, можно воспользоваться вспомогательной функцией values(), чтобы получить массив “енамов”. Их выведем через for.

println(Status.NEW.name)
println(Status.COOKING.ordinal)
for (i in Status.values()) println(i)

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

enum class Status(val id: Int) {
	NEW(101) {
		override fun getStatusDescription(): String {
			return "Заказ создан и ожидает оплаты"
		}
	},
	COOKING(102) {
		override fun getStatusDescription(): String {
			return "Заказ оплачен и начал готовиться"
		}
	},
	COMPLETED(103) {
		override fun getStatusDescription(): String {
			return "Заказ готов, можете забирать"
		}
	},
	ERROR(0) {
		override fun getStatusDescription(): String {
			return "Что-то пошло не так, мы уже разбираемся"
		}
	};

	abstract fun getStatusDescription(): String

}

Теперь проверим на примере нескольких статусов.

println(Status.NEW.getStatusDescription())
println(Status.COOKING.getStatusDescription())
println(Status.COMPLETED.getStatusDescription())

Все работает.

Функция TODO()

Вернемся к TODO(). Для справки. В Kotlin для функций без возвращаемого значения (когда не используем return) тоже существует специальный тип – Unit. Наведите курсор, например, на main и этот тип будет подсвечен в сигнатуре, как если бы эта функция возвращала какой-то базовый тип с данными.

Есть еще один тип, который не возвращает ничего – Nothing. И этот тип возвращается функцией TODO(). Но Nothing отличается от Unit своей функциональностью. Nothing говорит компилятору, что функция гарантированно не выполнится успешно.

Давайте провалимся внутрь TODO() и посмотрим на сигнатуру. Возвращаемый тип Nothing, при вызове этой функции выбросится исключение (ошибка) NotImplementedError. Запустим проект с присутствием TODO() в каком-нибудь методе и программа упадет с ошибкой.

TODO() обычно прописывается в качестве заглушки в процессе разработки. Дословный перевод понятен – ”надо сделать”. То есть мы можем объявить ряд функций, но временно с отсутствием внутренней логики. И проставить “тудушки”, чтобы обязательно реализовать их позже. Либо это делает среда разработки, как в данном случае. В аргументах TODO() можно еще указать развернутое пояснение, что нужно сделать. Тогда это сообщение выведется в исключении.

TODO можно использовать и в обычном комментарии, тогда изменится оформление комментария, можно использовать в качестве заметок. IntelliJ IDEA (как и Android Studio) распознает семантику TODO и все прописанные в проекте “тудушки” можно увидеть в специальной вкладке с инструментами.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *