Java vs. Kotlin для Android. День 1: спрыгиваем с Java
В очередной раз набирая на клавиатуре в Android Studio строчки кода с циклом for, поймал себя на мысли, что делал это множество раз и уже знаешь каждый следующий оператор, который идет дальше. Задача простая: выбрать из списка множество элементов, удовлетворяющее поставленному условию. Вроде бы ничего сложного, все просто: цикл по списку, внутри условие и добавление нового элемента в результирующий список. Еще один стандартный статический метод в классе с утилитами. Какой он там по счету уже...- 5ый или 10ый? Буквально
А тут еще на днях Петя/Ваня/Дэвид сказал, что они начинают новый проект под Android и будут писать его исключительно на Kotlin. Kotlin, а не Java. Чем этот язык лучше Java? Сколько уже было этих пустых никому не нужных попыток писать код под Android на PHP/C/C++/С#/Python, и где все это сейчас? Возможно, в этот раз все по-другому, и стоит обратить на него внимание? Ну что же, посмотрим, хотя я очень слабо верю во что-то путнее.
Разрабатывает его JetBrains и уже давненько, т.е. за это время их желание создать свой язык не иссякло. Это похвально и заслуживает уважения. Мне нравится их IDEA & AS. Более того, главной целью для себя они ставят полностью перевести разработку внутри компании с Java на Kotlin, и хотят, чтобы он, как и Java, использовался в industry. А вот некоторые называют его прямо «Swift под Android», вот же ж юмористы! И не просто люди с улицы, а сами разработчики Kotlin! Утверждают, что про NPE в рантайме можно забыть, что можно расширять стандартные классы из SDK, что можно лямбды и анонимные функции писать и даже передавать функции как параметры в методы! И даже точку с запятой убрали из синтаксиса (кстати, вот с этим я согласен на все 100%). А это прямо-таки из моего PHP-ного прошлого — String Templates — по таким вещам иногда скучаешь. Забавно, конечно, но вряд ли это все как-то может пригодиться в Android-разработке. Хм, у них есть плагин под IDEA/AS и даже специальный плагин под Андроид проекты. Вот это сейчас надо посмотреть.
Интересно выходит — Kotlin на 100% совместим с Java, и плагин позволяет java-код в один клик трансформировать в kotlin-код, и это как раз можно проверить достаточно быстро. Создадим-ка пустой Android-проект с одной Activity и Fragment внутри, и еще один простой дата-класс тоже не помешает.
Берем шаблонную Activity:
package com.testkotlin; import ... public class TestActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } }
и с помощью действия «Convert Java File to Kotlin File»:
на выходе получаем такой вот код:
package com.testkotlin; import ... class TestActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test) val toolbar = findViewById(R.id.toolbar) as Toolbar setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } } }
Выглядит неплохо, и вроде как все понятно. Получается, что из-за 100%-ой совместимости с Java новая TestActivity на Kotlin спокойно наследуется от AppCompatActivity, которая лежит в Support Library, а не в SDK. Ну что же, это довольно-таки серьезно облегчает переезд на рельсы Kotlin и делает использование Java-библиотек безболезненным. Безусловно это плюс, пожалуй, стоит записать очко в пользу Kotlin.
Теперь проверим, как это сработает на обычном дата-классе:
public class Message { private String text; private boolean isRead; public Message(String text, boolean isRead) { this.text = text; this.isRead = isRead; } public String getText() { return text; } public boolean isRead() { return isRead; } }
и конвертнем его в kotlin-файл:
class Message(val text: String, val isRead: Boolean)
Одна строка кода по сравнению с
Это ж на сколько время-то экономится?!?! Вот такого твиста я не ожидал, и, бегло (ну, как бегло...часика 2) пробежавшись по разделу «Classes and Objects» в документации, я узнал, что:
- для создания экземпляра класса нам не нужно ключевое слово new;
- никаких тебе extends или implements, для этого теперь используется «:»;
- класс может иметь один primary constructor и один/несколько secondary constructors;
- у primary constructor нет тела, но внутри него можно описать блок инициализации init;
- используются ключевые слова var (mutable property)/val (read-only property) для декларации и инициализации properties прямо в объявлении конструктора. Ключевые слова val\var используются и при объявлении обычных локальных переменных;
- OMG!, здесь есть named arguments и дефолтные значения для них;
- можно описывать кастомные accessors — круть! очень полезная штука;
- теперь не нужно явно вызывать getter/setter для получения/присвоения значения проперти — обращение происходит просто по имени — тоже здОрово!
- по умолчанию область видимости для классов/методов/пропертей — public;
- по умолчанию все классы в Kotlin final и нужно использовать open-аннотацию для класса, если необходимо иное поведение — к этому придется немного привыкать;
- интерфейсы теперь могу реализовывать методы!!! Т.е. класс может имплементить два интерфейса, которые имеют различную реализацию метода с одинаковой сигнатурой, и я смогу выбрать, где какой из методов использовать. Вот это реально круто! Очень давно об этом мечтал;
- вариативность женериков (generics) реализована с помощью in/out-кейвордов;
- реализовать anonymous inner classes можно через object expressions & object declarations;
- всеми любимый Singleton легко и просто реализуется с помощью object declaration;
- Java static methods == object declaration + companion object;
- делегирование реализовано на уровне декларации класса с помощью by-clause, компилятор сам сгенерирует необходимые дублирующие методы — любители делегирования возрадуйтесь, теперь не нужно дублировать вручную методы!
Отдельно стоит отметить, что фичи, затрагивающие сигнатуры методов, также распространяются и на обычные функции.
А еще можно взять java-код и скопипастить его в kotlin-файл и IDE сама предложит его конвертнуть. Кстати, на Java можно и в kotlin-файлах спокойно писать код, и все будет работать — это как еще один бонус 100%-й совместимости. Там же с помощью IDE полученный код можно упростить и привести его к kotlin-style. Это здорово поможет ускорить обучение и сдвинуть мышление в сторону Functional Programming(FP).
Все это выглядит здорово, местами для закоренелого object-oriented developer непривычно, но ведь красиво же! И, главное, что на выходе я получаю максимально оптимизированный java-код. Да уж, пессимизма по отношению к этому языку во мне поубавилось.
И я уже было собирался ложиться спать (на часах около завтра, пардон, сегодня. Еще и выспаться нужно успеть, и на проекте ждет меня куча бизнес-логики — опять циклы/условия и статические методы — а дедлайны никто не отменял. К тому же, будет что рассказать хлопцам за чашечкой кофе по Kotlin-у и непрозрачно намекнуть моему РМ-у, что было бы неплохо для Waverley (компания, где я работаю) начинать подыскивать Android-проект, где заказчику будет все равно на Kotlin-е мы его напишем или на Java. И разработчикам профит, и компания может себе в копилку еще одну технологию записать.