Kotlin — в начале было слово, тьма, объекты и коллекции

sashatinkoff
Aug 23, 2017 · 4 min read

Итак, в двух словах — к Kotlin’у я подбирался довольно продолжительное время… Ну как подбирался — ошивался где-то рядом, слышал что-то краем уха, но вот сесть и попробовать не решался, да и времени свободного не находилось (отмазки, отмазки…). В мае 2017 года на Google I/O сказали, что Kotlin отныне официально поддерживается гуглом наравне с Java (правда, это тоже не сильно меня подтолкнуло к близкому знакомству). Сказать по честности, я вообще думал “Зачем это надо, что старой доброй джавы не хватает?” и эти красные стопари держали меня до поры до времени.

Когда я все-таки открыл для себя ресурсы, посвященные котлину, в первую очередь я обратил внимание на следующие моменты:

  • Как-то местами более упрощенно описывается, чем в Java. А где-то наоборот, сложнее
  • Код действительно, как и предупреждали, пишется более “чистым” что ли, но меня это особенно не утруждало и в Java — в Android Studio, к примеру, отображение имплементации анонимных классов автоматом сокращается, так что код становится более “читабельным”. Плюс скрупулезное типизирование всего в коде меня более чем устраивало, и конструкции типа
    var name = “Sasha”
    сильно напоминало мне JavaScript

Ладно, ближе к теме повествования — я решил придумать какой-нибудь тривиальный пример, которых полно в моих проектах. Пусть будет некий абстрактный класс, его наследник, коллекция из объектов, над которой будем совершать какие-то действия. Итак, поехали.

Первое, что сбивает с толку — это способ объявления класса и конструктора по умолчанию. Если в Java мы писали что-то типа

public AbsBasic(String name, String birthday, boolean isMale)

То в Kotlin это выглядит по-другому. Итак, первое, на чем заскрипели заржавелые мозги — это следующая конструкция

AbsBasic(name: String, birthday: String, isMale: Boolean) : IBasicModel

Разберем по порядку что происходит. При объявлении класса необходимо указывать конструктор по умолчанию. Он может быть пустым

AbsBasic()

А может содержать в себе параметры по умолчанию, как в моем случае. Привычные конструкции “implements” и “extends” заменились на двоеточие и перечисление классов и интерфейсов, от которых происходит наследование. Кстати, не забываем учесть, что для базовых классов всегда должен быть всегда конструктор по умолчанию, который мы должны так же указать здесь. В Java это выглядело следующим образом

public AbsBasic(String name, String birthday, boolean isMale){
super(name, birthday, isMale);
}

Так что по большому счету ничего страшного не произошло — просто поменялся способ описания конструктора и передачи в него параметров.

Идем далее. Свойства классов. Они заполняются из дефолтного конструктора, но необходимо учесть в данном примере то, что в него передается дата в виде строки “yyyy-MM-dd”, а свойство класса birthday является типом Date. Поэтому в инициализации объекта в свойстве используется эта конструкция

var birthday: Date = SimpleDateFormat(“yyyy-MM-dd”).parse(birthday)

Как бы это выглядело в Java

public AbsBasic(String name, String birthday, boolean isMale){
this.birthday = new
SimpleDateFormat(“yyyy-MM-dd”).parse(birthday);
}

Другие свойства, описанные в классе, автоматически перехватывают значения из конструктора.

AbsBasic(name: String, birthday: String, isMale: Boolean)
{ var name: String = name …

Методы в Kotlin пишутся тоже иначе, но я обещаю — часик и вы привыкнете. В данном классе метод getAge вычисляет количество лет между текущей датой и днем рождения. Для простоты я использовал библиотеку JodaTime. Заметьте, кстати, у меня нет null-check в примере, об этом поговорим как-нибудь в другой раз. Тем временем, пойдем дальше и опишем класс, который наследуется от нашего абстрактного.

Тут уже конструкции, о которых я говорил выше — дефолтный конструктор класса BasicModel и передача данных в дефолтный(!!!) конструктор базового класса. Добавилось лишь свойство nick, которое может принимать значение null (о чем говорит знак вопроса), в этом случае мы не сможем использовать модификатор lateinit (см. раздел “Свойства с поздней инициализацией”).

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

Ничего интересного, как видим.

Давайте усложним немного наше погружение в дебри нового и используем паттерн проектирования декоратор. Следущий пункт задачи пусть звучит так

Надо сделать некий хелпер класс, в который мы будем передавать список объектов, который можно будет кастомно модифицировать (сортировать, фильтровать, обрезать) из других классов.

Давайте ближе к коду

Сколько непонятно, правда? Итак, начнем с декоратора, который представлен классом Top (не спрашивайте почему так, ответа тут нет). Данный класс содержит в себе один абстрактный метод “exec”, на вход которого подается один массив, а на выходе получается уже обработанный. У класса CollectionHelper опять же есть свойство top, в котором эта обработка должна быть указана.

Что касается этой конструкции

var filtered = top?.exec(models) ?: models

То здесь описан Элвис-оператор и на русский язык это может быть переведено следующим образом

List<BasicModel> filtered = models;
if(top != null) filtered = top.exec(models);

Ну или просто в одну строку

List<BasicModel> filtered = top != null ? top.exec(models) : models;

А что такое companion object?

Объявление объекта внутри класса может быть отмечено ключевым словом companion
https://kotlinlang.ru/docs/reference/object-declarations.html

Или это просто вызов статической функции init. То есть вот это вот все

class CollectionsHelper(var models: List<BasicModel>) {

companion object {
fun init(): CollectionsHelper {

В старой доброй Java выглядело бы как

class CollectionsHelper {

public static CollectionHelper init() {

А вот тут описание немного сложнее оказалось, признаюсь. В моем примере это просто вспомогательный метод, который создает список моделей и передает его в класс CollectionHelper возвращая его как результат. Это, к примеру, чтоб в Activity не плодить лишнего кода. Кстати, о ней — и это уже последний шаг в нашем сегодняшнем кратком экскурсе.

С чем мы незнакомы здесь — это с объявлением анонимного класса. Давайте приглядимся к этой строчке

collectionHelper.top = object : CollectionsHelper.Top()

Что в Java выглядело бы как

collectionHelper.top = new CollectionsHelper.Top() {

А это, к слову, тот самый декоратор, который модифицирует наш список. А именно сортирует по возрасту от старых к младшим. Что мы сможем увидеть в логах после запуска проекта, кстати

I/System.out: BasicModel(name=Santa Claus, isMale=true,age=65, birthday=1952–01–01)
I/System.out: BasicModel(name=Sasha, isMale=true,age=33, birthday=1983–09–22)
I/System.out: BasicModel(name=Snegurochka, isMale=false,age=27, birthday=1990–05–16)
I/System.out: BasicModel(name=Snegurochka Jr., isMale=false,age=19, birthday=1997–09–16)

Вот собственно и все, сорян за сумбурность. Можете кстати просто скачать пример с гитхаба, развернуть и потыкаться в коде самостоятельно.

https://github.com/sashatinkoff/KotlinExample

О разработке для Android

Нет единственно правильного решения любой задачи, их (решений) — множество. Я предлагаю вам одни варианты и не настаиваю на их использовании.

)
sashatinkoff

Written by

Пишу о разном с матом ем булку с маком никогда не бегал с автоматом

О разработке для Android

Нет единственно правильного решения любой задачи, их (решений) — множество. Я предлагаю вам одни варианты и не настаиваю на их использовании.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade