С 2016 года, после того, как я начал использовать Kotlin в Android-разработке, то ни разу не скучал по Java. В целом, Kotlin дал мне большее удовлетворение от разработки, чем Java для Android.

Тем не менее, время от времени я натыкаюсь на такие вещи, которых мне очень не хватает; того, как они были реализованы в Java. Ниже представлены 5 из них.

1. Тернарный оператор

В Java для простой булевой логики вместо того, чтобы писать if-else, мы можем использовать тернарный оператор, как показано ниже.

selector ? function1() : function2();

В Kotlin нам придется прибегнуть к традиционному if-else

if (selector) function1() else function2()

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

2. Статическая переменная

В Java, чтобы объявить статическую переменную в классе, достаточно использовать традиционный стиль языка C, просто поставив перед ней ключевое слово static.

class SomeClass {
    static int STATIC_INTEGER = 1
}

А в Kotlin мы должны использовать вложенный companion object.

class SomeClass {
    companion object {
        val static_integer = 1
    }   
}

Конечно, companion object в целом является более мощной функцией Kotlin в сравнении со static в Java, как утверждается в этом StackOverflow.

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

3. Побитовый оператор

В Java у нас есть лаконичный побитовый оператор. Применив его, я могу прописать более традиционный способ обработки битов, известный нам ещё со времён языка C.

int twoPowerOf = 2<<x;
int removeTransparent = 0xFF000000|color;

В Kotlin мы можем сделать это, используя побитовые ключевые слова.

val twoPowerOf = 2 shl x
var removeTransparent = 0xFF000000 or color

Не так уж и плохо. Просто это не совсем традиционно.

4. Цикл инкремента чисел с плавающей точкой  

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

В Java можно легко сделать это так:

for (float i = 0; i <= 10; i += 0.1) {
    // Do something with i
}

В Kotlin у нас нет традиционной возможности использовать цикл. Но мы все равно можем проделать это, как показано ниже, и вручную разделить:

(0..100).forEach {
    val i = it/10f
    // Do something with i
}

Или использовать цикл с предусловием (while-loop), как поделились  в этом StackOverflow. Недостатком while-loop является то, что управляющая переменная должна быть установлена и инкрементирована вне цикла.

var i = 0f
while (i <= 10f) {
    // Do something with i
    i += 0.1f
}

5. Объявление нескольких переменных

В Java можно объявить несколько переменных одного типа, как показано ниже:

int w = 8, x = 10, y = 12, z = 14;

В Kotlin, поскольку тип иногда выводится, лучшее, что мы можем сделать, это использовать точку с запятой:

val w = 8; val x = 10; val y = 12; val z = 14

Получилось чуть более многословно. Полагаю, что не стоит жаловаться, поскольку в Kotlin существует умная возможность неявного приведения типов при компиляции.

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


Перевод материала подготовлен для будущих учащихся на Java Developer Bootcamp. Также скоро состоится открытый урок «Разветывание и управление java-сервисами в облаке», на который приглашаем всех желающих. На этом занятии мы собираемся:

  • увидеть docker и kubernetes в работе;

  • научиться средствами github настраивать непрерывное развертывание сервиса в облаке;

  • познакомиться со средствами мониторинга состояние сервиса и используемых им ресурсов;

  • найти и исправить баги.

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


  1. ChPr
    04.02.2022 00:19
    +15

    for (float i = 0; i <= 10; i += 0.1) {
        // Do something with i
    }

    Отличный способ выстрелить себе в ногу на погрешностях округления. Этот пример считает до 9.900002, в то время как Kotlin версия до 10.0.


    Но отсутсвие нормального for-loop в Kotlin конечно убивает.


    1. vics001
      04.02.2022 02:45
      +4

      Есть куча циклов когда количество не так важно, этот цикл не бесконечный, а просто с "неопределенным" количеством итераций +- 1.

      for (float i = 0; i <= MAX; i += STEP) {

      plot(i, f(i));

      }


  1. jershell
    04.02.2022 00:40
    +12

    If же возвращает значение, зачем ещё нужен тернарный оператор? А если хочется проверить на null, есть :? fallback.

    У меня после прочтения сложилось впечатление, что автору не нравится что в Kotlin не все как в Java. Возможно что это у многих, кто переходит с одного языка на другой, что-то новое, а хочется чтобы было как раньше.


    1. vics001
      04.02.2022 02:49

      Честно скажу, циклы, ternary выглядит как отличие от Java, C++, Javascript, PHP, что уже серьезный набор языков :-)


      1. vabka
        05.02.2022 04:30
        +3

        Хотя например рядом есть Rust, Go, Haskell, Scala, F#, в которых нет тернарного оператора :)
        Думаю, во многих не си-подобных языках нет тернарного оператора.


        1. Maccimo
          06.02.2022 12:59

          Если верить хаскелевой вике, то в Хаскеле можно наколхозить тернарный оператор средствами языка.


  1. MEJIOMAH
    04.02.2022 02:24
    +6

    Если такой итератор нужен, то можно написать какую нибудь приблуду себе в проект, аля

    for (i in 1..10 step 0.1) {}

    infix fun IntRange.step(value: Double): Iterator


  1. boulder
    04.02.2022 03:03
    +1

    В "3. Побитовых операциях" у вас Java-код остался от предыдущего пункта. А в оригинале всё правильно.


    1. GolovinDS Автор
      04.02.2022 11:21
      +2

      Здравствуйте. Да, действительно ошибся.Все поправил


  1. vsb
    04.02.2022 08:38
    +6

    Для меня основная боль была это try-with-resources. И если для одного ресурса ещё можно использовать use(), то для нескольких - либо куча вложенности, либо свой велосипедик.


  1. Akon32
    04.02.2022 11:11
    +1

    В scala не хватает break / continue, особенно с меткой. break всё-таки реализован странным образом, но за 18 лет развития языка, наверно, можно было бы добавить некоторые типовые конструкции.

    Что хорошо сделано в scala и чего нет в java/kotlin/rust - оператор unapply, позволяющий парсить строки например так ( вместо ParseInt, ParseDouble у меня используются более короткие имена объектов) :

    object ParseInt{ def unapply(s:String):Option[Int] = {...} }
    object ParseDouble{ def unapply(s:String):Option[Double] = {...} }
    str split "\\s+" match{
      case Array(ParseInt(a),ParseDouble(b),c) => ... // a:Int, b:Double, c:String
      case Array(ParseInt(a),str) => ... // a:Int, str:String
      case Array(ParseDouble(a),"+",ParseDouble(b)) => a+b // a:Double,b:Double
    }


    1. transcengopher
      04.02.2022 19:12

      В Java тоже постепенно добавляют вот это: https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html


      Пока что (после того, как выйдет JDK 18) это выглядеть будет так: https://openjdk.java.net/jeps/420
      Unapply помечен как Future Work, видимо ещё пара релизов потребуется.


    1. Digitator
      04.02.2022 22:50

      break / continue : о, да!

      Особенно с учетом мультипарадигменности языка.

      Хоть перечисления в третьей скале добавили. Но похоже, все загнется из-за нового "пиши как угодно" синтаксиса.


  1. Amomum
    04.02.2022 13:16
    +2

    • Всегда ненавидел тернарный оператор (сперва потому что никак не мог запомнить, где у него какая ветка, потом за то, что он в С/C++ работает _не так же_ как if-else)

    • И после сишных `int * ptr, const a, *const * b` у меня дергается глаз при виде множества объявлений на одной строке

    Но понятно, что это все - вкусовщина.. до некоторой степени :)


  1. xd720p
    04.02.2022 18:07
    +2

    Тернарный оператор сделает Kotlin менее читаемым и сломает чёткое разделение, что конструкции со знаками вопроса и двоеточиями это про null-safety, а if else для возврата значения по условию. Какая конкретно польза будет в тернарном операторе как в Java я так и не понял за все эти годы)


  1. 13_beta2
    04.02.2022 18:58

    Никто не вспомнил неявное приведение к Int — ещё одно очень спорное решение. Ладно когда требуется присвоить значение меньшему типу, это требует явного приведения — логично. Но объявлять массив через (byte) 0x00, или не дать сравнивать someLong == 0 — вот это дичь какая-то.


  1. Maccimo
    05.02.2022 03:29
    +4

    В Kotlin мы можем сделать это, используя побитовые ключевые слова.

    val twoPowerOf = 2 shl x
    var removeTransparent = 0xFF000000 or color
    

    Не так уж и плохо. Просто это не совсем традиционно.

    Вот и выросло поколение, не видавшее традиционного Паскаля.


  1. Anarchist
    06.02.2022 18:09

    Если вы используете цикл, вы делаете что-то экзотическое. Или что-то неправильное.