Этот пост является вольным переводом статьи Kotlin and Swift. Is it a whole new era in Mobile Development? by Andrew Cherkashyn


Когда в Google объявили о том, что они теперь официально будут использовать Kotlin для разработки под Android, я, как и многие другие Android-разработчики, вздохнул с облегчением. Я еще раз зашел на официальный сайт Kotlin, чтобы перепроверить функционал/синтаксис и сравнить его с последней версией Swift, на котором сейчас пишу, и вдруг ощутил это: проходит одна эпоха и начинается новая, по крайней мере в мобильной разработке...



В Kotlin, как и в Swift довольно много синтаксического сахара, который снижает объемы обычной рутины (сравнение синтаксиса тут). Но что меня особенно радует — они оба, прям "из коробки", поддерживают новые парадигмы программирования. Особенно функциональное программирование.


Принципы функционального программирования, конечно, не являются чем-то новым в разработке, даже наоборот. Но теперь, когда есть официальная поддержка "из коробки" в разработке под iOS и Android — стоит пользоваться именно ими.


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


Java:


String[] mixedArray = new String[] { "4", "5", "a", "-2", "Str" };
int results = 0;
for (String element : mixedArray) {
    results += Integer.parseInt(element);
}

Теперь же все используют функциональный подход, чтобы сделать все то же самое за один вызов, и этот подход намного лучше:


Kotlin:


val mixedArray = arrayOf("4", "5", "a", "-2", "Str")
val results = mixedArray
    .filter { obj -> obj.toIntOrNull() != null }
    .map { x -> x.toInt() }
    .reduce { acc, x -> acc + x }

Swift:


let mixedArray = ["4", "5", "a", "-2", "Str"]
let results = mixedArray
    .filter({ (obj) -> Bool in return Int(obj) != nil })
    .map { (obj) -> Int in return Int(obj)! }
    .reduce(0, +)

Блоки были представлены Apple для Objective-C в 2010 году (iOS SDK 4.0), чтобы улучшить жизнь разработчиков и соответствовать анонимным классам в Java, которые могут быть использованы как коллбэки:


Пример блока в Objective-C:


void (^newBlock)(void) = ^{
    NSLog(@"New block is called");
};

Пример анонимного класса в Java:


(new CallbackClass() {
    @Override public void call() {
        Log.i(StaticTag, "Callback is called");
    }
});

Лямбда-выражения в Java были представлены в 2014, как часть JDK 8, но к сожалению они не были доступны Android-разработчикам, потому что Android SDK поддерживает только JDK версии 7 (поэтому и есть такие библиотеки, как retrolambda).


Теперь же оба языка полностью поддерживают такой подход: в Swift — "замыкания" (то же самое, что блоки в Objective-C), а у Kotlin есть поддержка "лямбд", которая работает в Android SDK:


Пример замыкания в Swift:


{ _ in
    print("Closure is called!")
}

Пример лямбды в Kotlin:


{
    println("lambda is called!")
}

Начиная с Xcode 4, где-то с 2011, Objective-C предоставляет однострочную инициализацию для массивов и словарей:


Пример инициализация в Swift:


let numbersArray = [2, 4, 1]
let dictionary = ["key1": "value1", "key2": "value2"

В JDK доступна только статическая инициализация, но нет способа инициализировать Map в одну строку. Функция Map.of которая позволяет это, была представлена только в JDK 9.


Пример статической инициализации в Java:


// Array
private static final int[] numbersArray = new int[] {2, 4, 1};
// Map
private static final Map<String, String> map;
static
{
    map = new HashMap<String, String>();
    map.put("key1", "value1");
    map.put("key2", "value2");
}

Но теперь Kotlin умеет делать так:


mapOf<String, String>("key1" to "value1", "key2" to "value2")

Еще одна вещь, которую я хочу выделить — Range операторы, которые делают вашу жизнь намного проще. Теперь вместо использования циклов for для простого обхода:


for (int i = 0; i < N; i++) {
    // Do something
}

Вы можете делать в Kotlin так:


for (i in 0..N-1) {
    // Do something
}

Или вот так в Swift:


for i in 0..<N {
    // Do Something
}

Стоит еще упомянуть о кортежах (tuples). Они дают определенную свободу во взаимодействии с другими компонентами и помогают избегать создания дополнительных классов.


Итак, глядя на все эти новые "фичи" и многие-многие другие вещи, которые не упомянуты в этой статье — я бы предположил, что новая эпоха уже началась. Теперь всем новичкам, которые начинают свой путь в мобильной разработке, будут доступны все эти функции прямо "из коробки", и они смогут сократить затраты на рутинную разработку бизнес-логики и управление приложением. И это намного важнее, чем писать сотни строк для того чтоб сделать простой кусок работы. Конечно, раньше вы могли просто поставить и настроить дополнительную библиотеку, такую как PromiseKit, ReactiveCocoa, RxJava и т.п. Но я верю, что доступность этих парадигм и принципов — будет побуждать новых разработчиков использовать именно их, что приведет нас к светлому будущему. :)


Спасибо за внимание! Я надеюсь вам было интересно или, как минимум, это пост дал вам свежие идеи. Я пытался написать коротко, но если вам нужны более конкретные примеры и/или у вас есть предложения/замечания — пожалуйста, напишите в комментариях!

Поделиться с друзьями
-->

Комментарии (55)


  1. noktigula
    01.06.2017 14:18
    +7

    Я люблю и использую Kotlin, но меня немного удивила приведенная аргументация.
    Не могли бы вы объяснить, почему в примере с циклом функциональный подход «намного лучше», и чем именно лучше?


    1. UnknownUser
      01.06.2017 15:10

      Подобных статей про javascript на хабре уже пруд пруди.
      Самый главный аргумент тут — «Во первых, это красиво!» )).


    1. s_suhanov
      01.06.2017 15:48
      -1

      Ну например у вас есть свойство:
      var myArray = [MyType]?


      В предложенном варианте можно написать что-то такое: myArray?.forEach { ... } а в классическом случае нужно сначала проверить его на nil, а потом уже идти циклом.


    1. Valle
      01.06.2017 18:48
      +3

      Лучше в основном тем что автор этого кода сразу становится вроде как умнее тех кто такую фигню не станет писать а напишет простой цикл вместо трех лямбд.


    1. hdfan2
      02.06.2017 07:37

      Кроме этого, ещё интересно, не получим ли мы три цикла и два создания промежуточных массивов. Или компилятор настолько умный, что сможет объединить все три операции в одну? Вот это меня больше всего напрягает в функциональщине — х.з. во что это раскроется, всё зависит от компилятора.


      1. semmaxim
        02.06.2017 08:42

        Да, получим. Чтобы был один цикл, нужно сначала сделать asSequence.
        Всё прекрасно известно, во что это раскроется. И зависит не от компилятора, а от стандартной библиотеки.


  1. errorok
    01.06.2017 15:10

    Когда в Google объявили о том, что они теперь официально будут использовать Kotlin для разработки под Android

    Можно узнать где и когда такое объявили? На сколько я знаю, объявили только о том, что теперь плагин для поддержки Kotlin входит в поставку Android Studio с версии 3.0. О том, что они теперь официально будут использовать Kotlin для разработки никто не говорил


    1. s_suhanov
      01.06.2017 15:23

      1. s_suhanov
        01.06.2017 15:48

        @errorok судя по минусам, ссылки вас не убедили. :)


        1. errorok
          01.06.2017 16:00
          +3

          У меня не полноценный аккаунт, я не могу ставить плюсы или минусы.
          Может я и не прав, так как мой английский не очень хорош. Но я не видел заявления Google что они будут официально на нём писать. Лишь сказали что теперь это ещё один официальный язык разработки под android. Наверное это можно понять как будто они теперь будут сами использовать Kotlin


          1. s_suhanov
            01.06.2017 16:05
            +1

            Ээээ. Так никто и не говорит, что они сами будут писать. Фраза "официально будут использовать Kotlin для разработки под Android" подразумевает, что теперь Kotlin официально поддерживается гуглом, как язык для разработки под Android другими разработчиками. :)


            1. JC_IIB
              01.06.2017 16:33
              +4

              Не соглашусь.
              "Гугл будет официально использовать котлин для разработки"
              и
              «поддерживается для разработки другими»
              это две огромные разницы. Потому что в первом случае — сам Гугл кодит на котлине, во втором (вашем) — другие кодят, что они, собственно и раньше могли делать.

              p.s. что-то слишком много пиара котлина в последнее время.


              1. s_suhanov
                01.06.2017 16:45
                -2

                Ну тут все зависит от трактовки слова "использовать". :)


                1. JC_IIB
                  01.06.2017 16:59
                  +1

                  Никоим образом. «Я буду использовать для разработки дачного дома пилу» — это значит только то, что я буду пилить сам.


    1. ikakus
      04.06.2017 10:41

      https://developer.android.com/samples/index.html?language=kotlin

      Вот примеры уже есть на оф сайте.


  1. VMichael
    01.06.2017 15:17

    for (int i = 0; i < N; i++) {
    // Do something
    }

    Вы можете делать в Kotlin так:

    for (i in 0..N-1) {
    // Do something
    }

    Или вот так в Swift:

    for i in 0..<N {
    // Do Something
    }


    Самый верхний интуитивно понятней (на мой взгляд не разработчика Java)
    А если нужен шаг 2, в нижних как задается?


    1. errorok
      01.06.2017 15:21
      +1

      В Kotlin так:

      for (i in 0..N step 2) {
      // Do something
      }
      


    1. noktigula
      01.06.2017 15:41

      До сих пор не понимаю, чем именно классический for не угодил господам из JetBrains…


      1. s_suhanov
        01.06.2017 15:49
        -1

        Ну например у вас есть свойство:
        var myArray = [MyType]?


        В предложенном варианте можно написать что-то такое: myArray?.forEach { ... } а в классическом случае нужно сначала проверить его на nil, а потом уже идти циклом.


    1. Daivest
      01.06.2017 16:49
      +1

      В Swift так:

      for i in stride(from: 0, to: N, by: 2) {
          // Do Something
      }
      
      В Kotlin эта конструкция поприятнее конечно…


      1. svanichkin
        02.06.2017 09:29
        -1

        for (int i = 0; i < N, i += 2) {
            // Do Something
        }
        

        Ещё приятнее классика…


        1. VMichael
          02.06.2017 12:08
          -1

          Спасибо всем ответившим.
          Признаться не пойму, ради чего затевалось.
          По мне так, каких супер улучшений не заметил.
          Так, косметика на любителя.


      1. KursoRUS
        05.06.2017 13:12

        В котлине вполне это же можно так написать:


        for (i in 0 until N step 2) {}

        until, step – это все функции из стандартной библиотеки.


        А для циклов по коллекции можно писать вот так:


        for (i in items.indices) {}

        Сам range оператор (который 0..N-1) в циклах лучше не использовать, очень легко -1 забыть.


  1. UnknownUser
    01.06.2017 15:39

    Почитал про Kotlin/Native.
    То есть, в принципе, бизнес логику написанную под Андроид можно будет легко портировать на iOS? Или я что то неправильно понимаю?


    1. molchanoviv
      01.06.2017 16:05
      +1

      Да. Если ты не будешь использовать при этом андроид-специфичных выражений. Это все-равно что написать на голом C++(без фреймворков) под винду и Linux. Вобщем написать то можно, но под каждую систему будут свои заморочки.


      1. s_suhanov
        01.06.2017 16:06

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


        1. molchanoviv
          01.06.2017 16:10

          Ну кроме выражений есть же еще и библиотеки которые хочешь-нехочешь затрагивают бизнес логику. А они для каждой платформы свои. А кросплатформенных библиотек нету. По крайней мере пока.


          1. UnknownUser
            02.06.2017 07:26

            Тут конечно, уже забуриваемся в частности, но можно пробовать такие вещи оборачивать в обёртки на Kotlin.


    1. lexd5
      01.06.2017 18:40
      +1

      Условно да, но все равно придется поддерживать два полностью дублирующихся проекта. Если есть необходимость, то лучше использовать что-то на одном языке, например Xamarin.iOS/Android. В этом случае вы получаете полную поддержку всего API (не путайте с Forms), а бизнес логика просто уйдет в кросс-платформу.


  1. noktigula
    01.06.2017 16:07
    +3

    Когда я вижу такие статьи, мне всегда кажется, что они пишутся PR отделом JetBrains и Apple (ни в коем случая не умаляю их заслуг, и никоим образом не хочу обидеть автора).
    Проблема в том, что вся мощь новых языков показывается на примерах уровня HelloWorld.
    Ну серьезно, неужели в примере 1 пример с filter, map и reduce выглядит привлекательнее обычного for? Из пушки по воробьям. При попытке продать Kotlin в проекты с большим legacy подобные примеры разбиваются в клочья.
    Другая проблема — излишняя восторженность от новых технологий как таковых. Ну не является Kotlin новой эпохой, нет в нем ничего такого, что было бы прямо киллер-фичей. На моей практике я никогда не был в ситуации, когда самая большая проблема проекта — это неправославный for или отсутствие лямбд. Основная проблема поддержки всегда кривая архитектура, спагетти, производительность — проблемы, которые Kotlin, увы, никак не решит (как не решит другой язык).
    Повторю еще раз — я люблю Kotlin, это прекрасный язык. Но статья никак не показывает, что это «новая эпоха, позволяющая сократить затраты на рутинную разработку бизнес-логики и управление приложением».


    1. s_suhanov
      01.06.2017 16:23
      -1

      Ну серьезно, неужели в примере 1 пример с filter, map и reduce выглядит привлекательнее обычного for?

      Просто представьте что вам нужно пройти циклом по опциональной коллекции, а не обычной. :)


      1. noktigula
        01.06.2017 16:26

        Я не отрицаю преимущества этого подхода, я лишь говорю, что пример в статье не совсем подходящий.


    1. JC_IIB
      01.06.2017 16:38
      +1

      Когда я вижу такие статьи, мне всегда кажется, что они пишутся PR отделом JetBrains


      Вы не одиноки в этом ощущении.

      Ну не является Kotlin новой эпохой, нет в нем ничего такого, что было бы прямо киллер-фичей.


      Абсолютно согласен. Но вокруг него прямо-таки искусственно создают buzz.


    1. Daivest
      01.06.2017 16:45
      +2

      Про первый пример, не знаю зачем автор использовал filter + map, в Swift например, гораздо красивее было бы

      let results = mixedArray
      .flatMap({ Int($0) })
      .reduce(0, +)


  1. chesno4eck
    01.06.2017 16:22
    +1

    Пишу на Swift'e, читал статьи про Kotlin и сложилось такое же впечатление, как у автора статьи.
    Это все действительно здорово. Писать одно удовольствие.


  1. yarric
    01.06.2017 20:01

    В итоге программисты под iOS будут программировать на новом быстроразвивающемся языке с хорошими нативными библиотеками и документацией, с поддержкой Apple, а программисты под Android — программировать на зоопарке из сторонних костылей, которые поддерживают все сообща, а поэтому мало кто конкретно.


  1. eugeneego
    01.06.2017 22:03
    +2

    let mixedArray = ["4", "5", "a", "-2", "Str"]
    let results = mixedArray
        .filter({ (obj) -> Bool in return Int(obj) != nil })
        .map { (obj) -> Int in return Int(obj)! }
        .reduce(0, +)

    За такое использование Swift обычно надо проводить серьезные разъяснительные беседы с автором.


    1. fly_style
      02.06.2017 18:25

      Налицо ФП головного мозга ;)


      1. s_suhanov
        02.06.2017 18:38

        Александр, вот что вы начинаете тут, а? :)


  1. hamMElion
    01.06.2017 22:52
    +1

    В JDK доступна только статическая инициализация, но нет способа инициализировать Map в одну строку.

    Нормального способа нет, но если через одно место, то можно!!!
    (Внимание! Никогда не используйте этот код в рабочих проектах! Это чисто поржать!)


            Map map = (Map) new ScriptEngineManager().getEngineByName("nashorn").eval("({a:'Hello',b:42,c:{c1:0.5,c2:true}})");
    
            System.out.println(map.entrySet()); // [a=Hello, b=42, c=[object Object]]
            System.out.println(map.get("a")); // Hello (String)
            System.out.println(map.get("b")); // 42 (Integer)
            System.out.println(((Map) map.get("c")).get("c1")); // 0.5 (Double)
            System.out.println(((Map) map.get("c")).get("c2")); // true (Boolean)

    Здесь бессовестно эксплуатируется библиотека javax.script и Project Nashorn, которые есть часть JDK Java 8.
    Вот такое извращение, но однострочник! =)


    1. yarric
      01.06.2017 22:56

      Интересно, какой будет оверхед на запуск скриптового движка.


    1. Valle
      02.06.2017 04:14
      +1

      Ну вообще можно в принципе с использованием инициалайзера: new HashMap<>(){{put(key, value);}}


      1. hamMElion
        02.06.2017 11:41

        Ну, в принципе, можно любую java-программу записать в одну строку, но вот однострочником она от этого не станет =)


        Ваш пример не масштабируется:


        new HashMap<>(){{
            put(key, value);
            put(key2, value2);
        }}

        Точкой с запятой (;) в java обозначают окончание высказывания, поэтому здесь нельзя говорить об однострочном высказывании. А вот dot-нотация — это однострочник.


  1. mezastel
    01.06.2017 23:59

    map{ x -> x.toInt() } можно писать как map(::toInt)


  1. kuzevanov
    02.06.2017 09:32
    -2

    Все это(и не только это) давно есть в C#, но к сожалению Apple на них ровняться не хочет…


    1. yarric
      02.06.2017 12:03

      Почему Apple должна равняться на C#?


      1. sborisov
        02.06.2017 13:27

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


        1. yarric
          02.06.2017 15:14
          -1

          Вообще-то Apple развивали собственную годную программную экосистему ещё когда MS пилил WinAPI. А C#, если вы не в курсе, появился как ответ MS на Java (тот самый NIH), и до сих пор тянет за собой ненужные для области применения C# фичи типа виртуальной машины.


  1. ChapayHabr
    02.06.2017 10:46
    +1

    Честно сказать как-то грустно!

    Заголовок «Kotlin и Swift. Новая эпоха в мобильной разработке?»

    А в статье пшык, просто вообще нет ничего что могло бы ему соответствовать!

    Ребята из Apple/Jetbrains старались столько лет работали, вложили столько труда и оценка всему этомуэто всего лишь стало меньше запятых, скобок и тд

    PS
    Грусть ((((((


    1. VMichael
      02.06.2017 12:13

      А так часто бывает.
      В любом, практически ИТ проекте, да и не только в ИТ.
      Сначала покрываются базовые, жизненно важные потребности.
      Потом начинаются фенечки, на которые вливаются много ресурсов, но отдача не сопоставима с первым этапом закрытия жизненно важных потребностей.
      Во внутренних проектах это еще виднее.
      Маркетологи придумывают какую нибудь фигню или отделы придумывают для себя отчетность, по все более микроскопическому поводу.
      P\S: Не оцениваю Kotlin, Swift, потому, как не работаю с ними. Так, пишу в общем.


      1. ChapayHabr
        02.06.2017 14:45
        +1

        Блин!!!
        Я не заметил что это перевод!
        Спасибо автору хабра, что перевел, он молодец )))

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


  1. ACPrikh
    02.06.2017 12:14
    +2

    Я знаю, что такое Do more. Но все равно перед глазами…
    image


    1. s_suhanov
      02.06.2017 15:27

      Вот это отлично. Пятьсплюсом вам. :)


  1. sborisov
    02.06.2017 13:21
    -1

    Мне кажется код Java (первого примера), настолько очевидный.
    Одного беглого взгляда достаточно, чтобы понять сколько памяти будет выделено, как будет работать цикл, будут ли создаваться временные объекты и т.д. и т.п.
    К сожалению своременные языки в погоне за функциональным стилем, теряют эту очевидную простоту.


  1. CoolMind
    02.06.2017 15:27

    А как же защита от NPE?