Итак, в двух словах — к 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