Почему Kotlin отстой

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


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


    Убогий for


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


    inline fun <T> For(it : Iterator<T>, cb : (T) -> Unit) {
      while (it.hasNext()) cb(it.next())
    }
    
    fun main(a : Array<String>) {
      val list = listOf(1, 3, 4, 12)
      println("for");   for (it in list) println(it)
      println("FOR");   For(list.iterator()) { println(it) }
    
      val arr = arrayOf(1, 3, 4, 12)
      println("a-for"); for (it in arr) println(it)
      println("a-FOR"); For(arr.iterator()) { println(it) }
    
      println("r-for"); for (it in 0..10) println(it)
      println("r-FOR"); For((0..10).iterator()) { println(it) }
    }

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


    Вопрос: зачем было вообще вводить это ключевое слово в язык и реализовывать жалкую породию частного случая цикла? Убогий цикл и без того уже есть.


    Собственно, бог бы с ним с этим недоделанным for-ом, если бы была альтернатива. Но ее нет. К сожалению, жизнь на итераторах не заканчивается, а когда приходится писать какие-то сложные циклы, то приходится жестоко страдать с убогим while-ом.


    Истерично-бессмысленная война с null-абле


    Может быть из-за того, что я стар, а может быть из-за того, что уже лет 25 успешно пишу на С, где (sic!) есть такая вещь как void*, я не испытываю никакого экстаза от повторения вслух шаблонных: "стрельба в ногу" и "ошибка на миллион". В результате, я просто не понимаю с чем воюют. Какая разница, когда хлопнется программа, на проверке аргументов или на их использовании?


    В чем соль декларирования null-safety Kotlin-ом, если он ее даже теоретически обеспечить не может? Значение null есть в самом языке, оно есть в Java, без инфраструктуры которой Kotlin, скажем прямо, не представляет никакого интереса. Как можно защититься от того, что используется за пределами языка и никак им не контролируется? Да никак. Это не более чем модная тенденция, уродование исходных текстов и регулярный геморой.


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


    var value : Int? = null
    
    fun F() : Int {
      if ( value != null ) return 0
      return value // Ошибка
    }

    Ошибка Smart cast to 'Int' is impossible, because 'value' is a mutable property that could have been changed by this time просто задалбывает. Хочется кого-то убить или что-то сломать.


    Где, как и кем эта проперть может быть модифицирована между двумя строчками??!!! Соседним тредом? Откуда взялась эта абсолютно бредовая уверенность компилятора в том, что каждая буква моей программы — это элемент многопоточной конкуренции? Даже в случае написания жестокого многопоточного кода пересечения тридов случаются на очень малом объеме текста программы, но из-за репрессивной заботы компилятора о такой возможности я имею гиморрой с клинописью постоянно.


    Кто придумал два восклицательных знака? Неуд! Два еще недостаточно взрывают мозг. Надо было пять. Или десять. И с обоих сторон. Так уж точно было бы понятно, где тут самый не кошерный и "небезопасный" код.


    var value : Int? = null
    
    fun F() : Int {
      if (value == null) return 0
      return when (Random().nextInt()) {
        3    -> value!! + 2
        12   -> value!! + 1
        5    -> value!! * 4
        else -> 0
      }
    }

    Самое пакостное, что жизнь никак не укладывается в красивые представления о "безопасном" коде тех, кому нужно каждый год продавать новую книгу о свежих тенденциях. К сожалению, null — это нормальное "неизвестное" состояние множества объектов, но при работе с ними постоянно приходится писать абсолютно ненужную клинопись.


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


    Ага, щаз.


    Java


    public class jHelper {
      public static jHelper jF() { return null; }
      public void M() {}
    }

    Kotlin


    fun F() {
      val a = jHelper.jF()
      a.M()  //Упс!
    }

    Это замечательно компилируется без каких-либо ошибок или предупреждений, запускается и с грохотом схлопытвается cо стандартным NullPointerException т.к. тут Kotlin не проверяет ничего и нигде. И где обеща..., тьфу, декларируемая безопасность?


    В общем, в сухом остатке я имею следующее:


    • регулярный гиморой с преодолением надуманных проблем в моем коде;
    • постоянные приседания с !! при работе с nullable типами в моем коде;
    • постоянный оверхед, генерируемый компилятором на проверках всех параметров функций и при установке любых значений в моем коде;
    • нулевую безопасность для любых данных пришедших извне;

    Т.е. весь гиморрой только в той части, которую я знаю и контролирую, а все наружнее молча посыпется при первой же возможности. Зашибись, зачет!


    Зато все красиво и по феншую.


    Почему присваивание — это не выражение?


    Даже if это убогое, но выражение, а присваиванию эту возможность отрезали. Почему я не могу написать так?


    var value = 10
    
    fun F() : Int {
      return value = 0 // Ошибка
    }

    или так:


    var v1 = 1
    var v2 = 1
    var v3 = 1
    
    fun F() {
      v1 = v2 = v3 = 0 // Ошибка
    }

    Что в этом коде криминального? Хотя, я наверное, догадаюсь. Защищаем пользователя от if (v=20)?.. Но, врядли, т.к. это просто не соберется без автоматического приведения типов, которого у Kotlin, опять-же, нет. Сдаюсь. Кто знает ответ?


    Чем не угодил оператор "?:"?


    За что ампутировали оператор "?:"?


    Что усмотрели вредного в таких конструкциях?


    value != 0 ? "Y" : "N"

    С if все замечательно:


    if (value != 0) "Y" else "N"

    кроме полной альтернативности (где такое еще есть?) и того, что часто побочная писанина if () else места занимает больше, чем само выражение.


    За что убили автоматическое приведение типов?


    Да, тотальное приведение типов друг к другу — это чистое и незамутненное зло. Я обоими руками против того, чтобы плодить ребусы, к которым приводит взаимное преобразование чисел и строк. В принципе, я даже за то, чтобы различать целочисленное и плавучку. Но зачем было совсем все-то вырезать?! Почему нельзя использовать стандартные и общепринятые правила приведения типов, которые существуют в подавляющем большинстве языков?


    Ну ладно, пусть даже отрезали. Привет Pascal. Но зачем в документации-то врать про «there are no implicit widening conversions for numbers»? Где оно "are no", если такое замечательно собирается?


    val i = 10
    val l = 12L
    val f = 12.1
    
    val l1 = i+100/l-f

    Где ожидаемый хардкор?!


    val l1 = i.toDouble() + 100.toDouble() / l.toDouble() - f

    Т.е. авто-приведения типов нет… хотя… оно как-бы есть… но только для выражений… и еще для констант. А вот если передать в качестве параметра надо переменную или там присвоить в переменную без вычислений — тут уже ручная гребля в санях. Ведь это так принципиально, и нужно акцентировать все внимание на том, что вот из этого Int получается именно Long, а из этого Float именно Double.


    Я прямо чувствую, как количество ошибок в моей программе стремительно тает от такой заботы обо мне.


    Хотел бы я еще заикнуться про крайне желательное:


    val c : SomeClass? = null
    
    if ( c ) "not-null"
    if ( !c ) "is-null"

    но не буду т.к. опасаюсь за свою жизнь.


    Недо-typedef


    Давно просили прикрутить к Kotlin псевдонимы. Прикрутили. Я не знаю в каких случаях люди это планируют использовать но, на мой взгляд, толку от такой реализации примерно ноль. Назвали бы эту конструкцию макросом — у меня притензий бы не было, а так… обман какой-то.


    Давайте разберемся в каких ситуациях вообще нужны псевдонимы в каком-нибудь языке. Я могу предположить следующее их применение:


    1. Создание альтернативного имени для существующего класса. Задача довольно бестолковая но, возможно, кому-то пригодится. С этим существующие псевдонимы справляются полностью.


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


    3. Уменьшение писанины при использовании шаблонных типов. Эта задача самая полезная и часто используемая. Существующие псевдонимы могут решить только описательную ее часть (см п.1), т.е. их можно использовать для описания типа переменных, параметров, возвращаемого значения и создать объект такого (базового) типа. Певдоним для шаблонного типа нельзя использовать для приведения или проверки типа объекта.

    На практике мы имеем следующее:


    typealias aI = SuperPuperClassA
    typealias pSI = Pair<String,Int>
    typealias pIS = Pair<Int,String>
    typealias pOTHER = Pair<String,Int>
    typealias aS = List<String>
    
    class SuperPuperClassA {
      fun F() = pSI("",10)
    }
    
    fun main(a : Array<String>) {
      val a = aI()
      val i1 = a.F()
      val i2 : Pair<*,*> = a.F()
      val i3 : Any = a.F()
    
      //Этот код собирается и условие выполняется
      if ( i1 is pSI ) println("ok")
      if ( i1 is pOTHER ) println("ok")
    
      //Этот код НЕ собирается
      if ( i1 is pIS ) println("not compile")
      if ( i2 is pSI ) println("not compile")
      if ( i2 is pIS ) println("not compile")
      if ( i3 is pSI ) println("not compile")
      if ( i3 is pIS ) println("not compile")
    }

    Обратите внимание на то, что в обоих строках где код собирается условие выполнится. Т.к. псевдоним не является полноценным типом, то различить их невозможно. Собственно, Kotlin мог бы их различать хотя бы в случаях, как в этом примере (весь код с явными и известными типами), но, видимо, нет желания.


    Код, которые не собирается, имеет одну и ту же проблему: "Cannot check for instance of erased type". Проблема в недоразвитости (попросту отсутствии) шаблонов в рантайме JVM.


    Итого.


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


    А, да, я говорил что псевдонимы можно описывать только глобальные, вне любого класса?


    Nested and local type aliases are not supported


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


    Убогие шаблоны


    Шаблоны (generics) в Java вообще и в Kotlin в частности убоги и причина абсолютно одна и та же: JVM ничего не знает о шаблонах и все эти треугольные скобки в языке не более чем навесное синтаксическое украшательство.


    Бог с ней с Java т.к. меня лично ее проблемы не волнуют. Меня волнует ущербность шаблонов конкретно в Kotlin, который позиционируется как другой язык, а не препроцессор для Java и, в результате, кивать на ее недостатки по меньшей мере бессмысленно.


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


    Как Вам такой ребус:


    /*00*/ class C<T>(val value : Any) {
    /*01*/   fun F() : T {
    /*02*/     try {
    /*03*/       val v = value as T //Предупреждение компилятора "Unchecked cast: Any to T"
    /*04*/       return v
    /*05*/     } catch(ex : RuntimeException) {
    /*06*/       println("Incompatible")
    /*07*/       // Хак для иллюстрации того, что эксепшин будет съеден и не пойдет дальше
    /*08*/       return 0 as T
    /*09*/     }
    /*10*/   }
    /*11*/ }
    /*12*/ 
    /*13*/ fun fTest() {
    /*14*/   val a = C<Int>( 12.789 )
    /*15*/   println( "rc: ${a.F()}" )
    /*16*/ 
    /*17*/   val b = C<Int>( "12.123" )
    /*18*/   println( "rc: ${b.F()}" )
    /*19*/ }

    В этом коде, в классе "С" делается попытка проверить совместим ли тип объекта с типом шаблона.


    Внимание, вопрос: как отработает этот код?


    Варианты ответов:


    1. Не соберется вообще
    2. Соберется, выполнится и напечатает "12", "12"
    3. Соберется, выполнится и напечатает "12", "Incompatible"
    4. Соберется, выполнится и напечатает "12.789", "12.123"
    5. Хлопнется при запуске внутри функции "C::F" (на какой строке?)
    6. Хлопнется при запуске внутри функции "fTest" (на какой строке?)

    Правильный ответ

    Правильный ответ: хлопнется при запуске внутри функции "fTest" на строке 18


    rc: 12
    
    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
        at jm.test.ktest.KMainKt.fT(kMain.kt:18)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

    Следующий конкурс: кто может объяснить почему это происходит?


    1. Почему не упало на первом вызове, где передается Double вместо Int?
    2. Почему не отработал блок try/catch?
    3. Как ошибка кастинга с ПРАВИЛЬНЫМИ типами смогла вообще доехать до кода используещего функцию "C::F"?

    Под капотом

    Кратеньно, выводы делайте сами.


    Вот код, который генерирует Kotlin для проверки типа внутри "C::F":


    // val v = value as T
    
    GETFIELD jm/test/ktest/C.value : Ljava/lang/Object;
    CHECKCAST java/lang/Object
    ASTORE 1

    Если очень сильно подумать (или заранее знать что оно неработоспособно), объяснить почему именно CHECKCAST Object можно. Сложнее объяснить зачем вообще этот код генерировать т.к. он абсолютная пустышка всегда, но это вопрос уже совсем к другой части компилятора.


    А вот код, который генерируется при вызове функции "C::F":


    LINENUMBER 18 L6
    ALOAD 1
    INVOKEVIRTUAL jm/test/ktest/C.F ()Ljava/lang/Object;
    CHECKCAST java/lang/Number

    Опять-же, если очень сильно думать (или знать заранее), то можно объяснить наличие правильных типов в этом месте, но лично для меня сам факт наличия проверки типов после вызова функции был неожиданностью. И да: Kotlin, оказывается, генерирует проверку типа снаружи при каждом использовании шаблонного результата для любого класса.


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


    Я все понимаю: шаблонов в Java нет и все подобное. Этот пункт, скорее всего, не появился бы вообще, если бы нормальную работу с шаблонами нельзя было бы реализовать в принципе никогда и нигде… Но вот у меня перед глазами яркий пример — VCL. Фирма Borland в богом забытом году умудрилась прикрутить не к чему-нибудь, а к С и Pascal настолько мощное RTTI, что альтернатив ему не существует до сих пор. А тут не машинный код, тут Java и обеспечить в ней полнофункциональное использование шаблонов в своем, Kotlin-овском коде можно. Но его нет. В результате, язык вроде бы и другой, а ситуация, из-за синтаксического разнообразия, еще хуже чем в самой Java.


    Напишем аналог шаблона из ребуса на Java.


    public class jTest<T> {
      Object value;
    
      jTest( Object v ) { value = v; }
    
      public T F() { return (T)value; } //Предупреждение компилятора "Unchecked cast"
    
      public static void Test() {
        jTest<Integer> a = new jTest<Integer>( 12.123 );
        System.out.print( "rcA: " );
        System.out.print( a.F() );
    
        jTest<Integer> b = new jTest<Integer>( "12.789" );
        System.out.print( "\nrcB: " );
        System.out.print( b.F() );
    
        System.out.print( "\n" );
      }
    }

    И попробуем его вызвать из Kotlin и Java.


    fun fTJ_1() {
      val a = jTest<Int>( 12.123 )
      println( "rc: ${a.F()}" )
    
      val b = jTest<Int>( "12.789" )
      println( "rc: ${b.F()}" )
    }
    
    fun fTJ_2() {
      jTest.Test()
    }

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


    1. и шаблон и его использование реализовано на Kotlin;
    2. шаблон на Java, а его использование на Kotlin;
    3. и шаблон и реализацая на Java;

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


    Варианты:


    • Все примеры отработают одинаково.
    • Все примеры отработают по разному.
    • Все примеры, где реализация написана на Kotlin отработают одинаково, а с Java по другому.

    Правильный ответ

    Правильный ответ: все три варианта поведут себя по разному


    1. Kotlin:


      rc: 12
      Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

    2. Kotlin->Java:


      Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer

    3. Java:
      rcA: 12.123
      rcB: 12.789

    Почему так а не иначе? А это будет домашнее задание.


    Нет синтаксиса для описания структур


    Если взять абсолютно любой сравнымый язык (хоть саму Java, хоть Scala, Groovy и множество прочих от Lua до, даже, С++) то в них во всех сделано так, чтобы было удобно описывать структуры данных в коде программы.


    Kotlin — это единственный известный мне язык, где синтаксиса для описания структур данных нет вообще. Есть (грубо говоря) всего три функции: listOf, mapOf и arrayOf.


    Если с массивами и спискамми синтаксис громоздок, но как-то структурируется зрительно:


      val iArr1 = arrayOf(1, 2, 3)
      val iArr2 = arrayOf( arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3) )
      val iArr3 = arrayOf(
        arrayOf(arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3)),
        arrayOf(arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3)),
        arrayOf(arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3))
        )

    то с картами все значительно печальнее:


      val tree = mapOf(
        Pair("dir1", mapOf(Pair("file1", 0), Pair("file2", 1))),
        Pair("dir2", mapOf(
          Pair("dir21", mapOf(Pair("file1", 0), Pair("file2", 1))),
          Pair("dir22", mapOf(Pair("file1", 0), Pair("file2", 1))))) )

    Я не знаю как именно другие пользователи описывают константные данные, но лично я испытываю жестокий дискомфорт при попытке использовать что-то сложнее одномерного списка.


    Понятно, что можно засунуть данные в любой подходящий формат (хотя бы тот же JSON и оттуда ее прочитать), однако...


    1. Засовывать каждый десяток строк в отдельный файл только для того, чтобы иметь возможность ими наглядно манипулировать — это как-то избыточно (хотя именно так и приходится делать).
    2. Усилия по написанию структуры кода прямо в программе и во внешнем файле, с последующим их чтением, просто несопоставимы.
    3. В случае изменения структуры данных приходится, помимо кода, править гору совершенно лишнего текста по обслуживанию его загрузки.

    В общем, концепция минимализма — это круто, но аццки неудобно.


    ПС: В виде отдельного гвоздя в голову я пожелаю кому-нибудь написать библиотеку для работы с матрицами. Зато научитесь понимать отличать Array<Array<Array<Array<Double>>>> и Array<Array<Array<Double>>> с первого взгляда и с любого расстояния.

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 154

      +4
      вещи, которые мне лично действительно усложняют жизнь.

      Правильно ли я понял что наличие на ваш взгляд бесполезного for-цикла усложняет вам жизнь? Или все это сарказм с начала и до конца?

        +6

        Усложняет, разумеется, не наличие бесполезного цикла, а отсутствие полезного.

          +3

          Усложняет, разумеется, не наличие бесполезного цикла, а отсутствие полезного.
          Фор — это очень удобная и краткая конструкция с двумя выражениями и одним условием.
          Она короткая и очень наглядная.
          Для меня лично, разворачивание его в while с отдельным описанием переменных, значительно снижает читаемость кода.

            –1

            Имхо, за использование цикла for для чего-либо кроме итерации по элементам коллекции надо бить по рукам во всех языках. По семантике и смыслу for это именно цикл итерирования. Всё остальное и должно делаться через while.

              0

              Вот только for (int i=x; i>0; i=(i-1)&i) — это тоже своего рода итерация по элементам коллекции...

                0

                Что значит "своего рода"? Если это индексы, то можно использовать обычный итеративный for. Честно говоря, проблема мне вообще кажется надуманой, хотя я и не пишу на kotlin. Если это не итерация, то нефиг использовать для этого for.

                  0
                  // котлин-вей (как я себе его представляю)
                  generateSequence(x) { (it - 1) and it }.takeWhile { it > 0 }.forEach { 
                        // todo
                  }
                  
                  // для тех кто экономит на итераторах
                  var i = x ; while (i > 0) { 
                      i = (i - 1) and i
                      // todo
                  }
                  
                  // если очень хочется в три блока как в старые добрые
                  inline fun <T> oldScoolFor(initValue: T, 
                                             condition: (T) -> Boolean, 
                                             next: (T) -> T, 
                                             body: (T) -> Unit) {
                      var i = initValue
                      while (condition(i)) {
                          i = next(i)
                          body(i)
                      }
                  }
                  
                  oldScoolFor(x, { x > 0 }, { (it - 1) and it }) {
                      // todo
                  }
                  
                    0
                    Можно и так. Только у вас все три цикла написаны с ошибками…
                      –1
                      С фатальными я полагаю?
            +1

            Ceylon и тот поддерживает "нормальные" generics, а Kotlin увы...

              +1

              Действительно, в нем декларируется "Ceylon's type system is fully reified at runtime" и в качестве примера приводят именно "if (is Map<String,Object> map)".
              Интересно, какой ценой они этого добились?

                +5
                Ценой потери совместимости с java.
                  0

                  Я цейлон не изучал, но на их сайте указан таргет JVM и в описательной части приведены примеры для использвания с Java EE. Прямые их примеры использования жавы тут:
                  https://github.com/ceylon/ceylon-examples-jdk
                  Как это может работать без совместимости с Java?

                    0
                    Java != JVM
                    Не уверен про случай цейлона, но думаю вы не можете без конвертации использовать типы цейлона с reifiend дженериками в Java
                    • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  вероятно, созданием дополнительных классов для каждой встреченной комбинации тИповых параметров.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +3
                    используете цейлон в продакшене?
                    • НЛО прилетело и опубликовало эту надпись здесь
                  +6
                  Из обычного for можно выйти по break или continue. С лямбда-блоком такого не выйдет.
                    +1

                    Согласен, break реализовать в коде сложно.

                      –2
                      Легко. Киньте в Break исключение без стектрейса и поймайте его в For.
                        +4
                        Исключение — штука тоже совсем не дешёвая, и лишний try-catch блок может убить производительность в критических местах. Ну и про то, что это дичайший костыль — я вообще молчу.
                          0
                          Она не дешевая только из-за сбора трейса.
                            +2
                            а можете подробнее рассказать про «недешевость» исключения? за что так минусуют предыдущего оратора?
                              +3

                              Даже try-catch блок, exception в котором никогда не бросается совсем не бесплатен, он мешает делать оптимизации во время выполнения, соответственно, он может стоит от ничего до сильного проседания производительности. Поэтому нет, исключения сильно сажают производительность не только из-за сбора трейса, но и из-за того что вместо заставляете JVM отключать многие оптимизации кода.
                              В целом, использовать исключения для выхода из цикла это почти всегда плохо.

                                0

                                Да что вы там за низкоуровневый мега-производитеььный код пишете на Java? Задолбали уже с этой темой медленного Exception. Масса операций (запрос к БД, чтение с диска, обращение к сети) на несколько порядков медленнее генерации и ловли исключения, тем не менее все эти операции выполняются по тысяче раз в секунду. Никто не будет в здравом рассудке генерить сотни тысяч исключений в секунду, а жалкая пара сотен вообще никак не скажется на производительности.

                                  0
                                  Да что вы там за низкоуровневый мега-производитеььный код пишете на Java

                                  Приходилось писать для BigData (Hadoop, Apache Spark), HighLoad (веб сервис отвечающий на десятки и сотни тысяч запросов в секунду). Там экономия даже 10% позволяет покупать не 100 серверов, а 90. Сразу отвечаю, предлагать использовать C/С++ для этого не стоит, по многим причинам правильно настроенная Java предпочтительнее С++ и прочих относительно низкоуровневых языков (Java на самом деле достаточно производительная, если у программистов прямые руки, и к тому же очень надежна, если руки тоже не из одного места).


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

                                  Это только кажется, я видел в реальных приложениях алгоритмы вида "сделаем sql select из таблица с миллионными записей, потом в Java отфильтруем по имени и возьмем десяток записей". Причем писали их опытные программисты, проблема в том что сначала была таблица на сотню записей и так было вроде бы проще, потом записей стали миллионы, а память приложение стало есть со страшной силой и приложение отвечало медленно и печально.


                                  Автор выше предлагает выходить из for-а через кидание exception'a, предположим автор реализовал это для for-а какой-нибудь функции, а потом другой программист использовал её в цикле сотню тысяч раз (не вникая в детали реализации функции) и вот у нас уже неожиданно в система появилось сотня тысяч исключений в секунду. Причем об этом мы узнаем только когда у нас система начнет "провисать".


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

                                    +2
                                    предположим автор реализовал это для for-а какой-нибудь функции, а потом другой программист использовал её в цикле сотню тысяч раз

                                    Т.е. все-таки проблема не в самом Exception'е как таковом (вызванном один раз), а в том, что не надо его кидать по 100 тыщ раз. Я именно об этом и говорил

                        –1
                        Выйти можно, continue придется делать через условие или по другому построив цикл (например отфильтровав и т.п.)
                        https://kotlinlang.org/docs/reference/returns.html#return-at-labels
                          +3
                          Вместо break можно написать вот такую конструкцию:

                          (0..10).forEach one@ {
                              (0..10).forEach two@ {
                                  if (0 == 0) {
                                      return@one
                                  }
                              }
                          }
                          

                            0
                            Более развёрнутый вариант:

                            fun foo() {
                            
                                fun loop() {
                                    (0..10).forEach one@ { i ->
                                        (0..100).forEach two@ { j ->
                                            if (j == 5) {
                                                return@two  // continue для вложенного
                                                return@one  // break для вложенного или continue для внешнего
                                                return@loop // break для внешнего
                                            }
                                            print("$i, $j\n")
                                        }
                                    }
                                }
                            
                                loop()
                            }
                            
                          +1
                          import org.jetbrains.annotations.Nullable;
                          
                          public class jHelper {
                            public static @Nullable jHelper jF() { return null; }
                            public void M() {}
                          }
                          


                          
                          fun main(args: Array<String>) {
                              val a = jHelper.jF()
                              a.M() // Compile error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type jHelper?
                          }
                          
                            +5

                            ИМХО тут автор как раз прав — сакральный смысл таких "манипуляций" стремится к нулю. В тот же Spring таких аннотаций не понаставишь. Логичнее как раз считать всё "чужеродное" максимально "небезопасным", если не указано иное.
                            Вообще странно. Год или полтора назад я читал, что в Kotlin специально решили считать все ссылки родом из Java — Nullable по умолчанию. И даже гоняли над JRE специальный верификатор, который составлял "карту аннотаций". Решили не заморачиваться, что ли?

                              +2

                              Наоборот.
                              Специально для внешнего кода убрали проверки.
                              Находил описание, когда наступил на эти грабли в каком-то парсере и начал искать почему в коде нет проверок.
                              Полное содержимое кейса я не помню, но смысл был в том, что при наличии проверок ломался какой-то код.
                              Сейчас нулабле-тип объявленный в Kotlin имеет тип "?", а из Java "!".


                              //KOTLIN:
                              fun sNULL() : String? = ""
                              
                              //JAVA:
                                public static String F1() { return null; }
                              
                                val k = sNULL() // тип переменной String?
                                val j = jTest.F1() // тип переменной String!

                              Для первого проверки производятся, и выдаются все предупреждения, а для второго нет.

                                +4

                                Тогда смысл "не-нуллабельности" вообще нулевой получается — с учётом что 95% нынешнего кода не на Kotlin. Я при работе с внешним кодом не получаю от компилятора абсолютно никакой помощи, и точно так же могу схлопотать NPE почти в любой момент. Смысл был бы как раз заставить проверять инварианты на внешнем коде или прогонять какой-то статический чекер. А так синтаксис покрасивше, не более.

                                  +4

                                  Смысла нет
                                  Об том и спич :)

                                    +7

                                    У поддержки null типов в Котлин в том виде в котором она там есть два очень весомых "смысла":


                                    1) Устраняется "распространение" null-ов. Основная боль в Java в том что null может быть незаметно передан из функцию в функцию, из поля в поле, и NPE вылетит совсем не там где null возник. Для этого рекомендуется всюду ставить null-проверки, но не все это делают. Kotlin имеет null-assert-ы во всех публичных методах, и распространение null-ов предотвращается на самом раннем этапе.


                                    2) Null-типы позволяют декларировать nullability явно и удобно (в отличие от аннотаций). Это сильно упрощает использование кода на Kotlin.

                                      0

                                      Чегож вы все читаете не то что написано, а то, на что знаете ответ?...


                                      Где написано что нулевые типы в котлин — это плохо?
                                      Где написано что я не понимаю откуда берется null?


                                      Претензии к текущей реализации 2:


                                      1. Ошибка компиляции, исключение из случаев safe-check, фактическая польза от которой, на практике, составляет мизерную часть тех мест в которых ее выбразывает компилятор, а во всех остальных она вызывает только раздражение.


                                      2. То, что все эти проверки НЕ РАБОТАЮТ с данными, пришедшими из жавы!
                                        А далее можете цитировать себя сами со всем обилием возможных проблем.
                                        Вот пример именно Ваших слов с последствиями.

                                        fun Dispatch( obj:GuiObject) {
                                        ... // тут будет проверка параметра на null
                                        }
                                      
                                        Dispatch( JavaCodeClass.getCurObject() )  // Тут проверки НЕ будет

                                      В текущей архитектуре код хлопнется на проверке парамеров внутри "Dispatch" в РАНТАЙМЕ, т.е. все танцы с "?" и "!!", которые я вынужден использовать в исходниках, оказываются совершенно бессмысленными.

                                  +3
                                  Решили не заморачиваться, что ли?
                                  Поняли, что задача в общем виде нерешаемая из-за generics. Андрей Бреслав — Kotlin: самое сложное — совместимость
                                  +2

                                  Я так понимаю, ты намекаешь на то, что никто не мешает исправить существующие классы Java и добавить в них недостающие аннотации?

                                    0
                                    Готовые аннотации к классам JDK уже лежат в IDEA\lib\jdkAnnotations.jar. Для других библиотек в IDEA можно добавить вручную через механизм внешних аннотаций.
                                      0

                                      Ясно.
                                      Ты серьезно считаешь, что работу с миллионом существующих библиотек Java я долен начинать с их реверс-инжиниринга в попытках понять как они работают и чем чревато их использование с последующей их модификацией наружними аннотациями?
                                      Ты сам-то так пробовл делать?
                                      Сколько времени остается на ту работу за которую платят зарплату?

                                        +3

                                        В IDEA внешнюю аннотацию можно добавить в пару кликов при вызове библиотечного метода. Не трогая те методы, которые не нужны. Если же вы не разобрались, может ли вернуть null используемый метод, то вместо работы, за которую платят зарплату, вам вскоре придётся дебажить NPE в продакшне.

                                          0

                                          Эти "пара кликов" займут 0.0001% времени в задаче "понять куда, как и когда их надо воткнуть".
                                          Я подозреваю, что задачами по адаптации чужого кода, написано непойми как хз сколько лет назад, без коментариев и, возможно, даже без исходников Вы если и занимались, то не часто.
                                          Просто поверьте, что тупое втыкание аннотаций к чужому коду может привести к проблемам по крайней мере не менее сложным чем "дебажить нпе в продакшине"

                                            +1
                                            Эти "пара кликов" займут 0.0001% времени в задаче "понять куда, как и когда их надо воткнуть".

                                            Именно так. Пренебрежимо малые затраты. Но понять вам всё-равно придётся. Или нет? Неужели вы из тех, кто — фигак, фигак и в продакшн? Тогда, конечно, понимать не надо.

                                            • НЛО прилетело и опубликовало эту надпись здесь
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                    0
                                    Не считаю язык плохим, но в принципе, с каждым приведенным аргументом согласен, тоже раздражают эти вещи жутко.

                                    Мне непонятно главным образом 2 вещи:
                                    1) Зачем делать что-то хуже, чем уже было в Java (for, ?: оператор, приведение типов и т.д.)
                                    2) Почему создатели языка считают, что только компилятор должен решать как обезопасить код, а не программист?

                                    Серьезно, эта ошибка просто мозг выносит.
                                    Smart cast to 'Int' is impossible, because 'value' is a mutable property that could have been changed by this time.
                                    Я знаю про более идиоматический вариант с ?.let{ }, но не считаю, что в таких ситуациях этот подход повышает читаемость, за что так борется Kotlin.
                                      +8
                                      2) Почему создатели языка считают, что только компилятор должен решать как обезопасить код, а не программист?

                                      Потому что в современном мире компилятор должен пытаться максимально решить проблемы на стадии компиляции, а не оставлять их на рантайм. Пришли времена, когда компилятор может быть действительно умным, а не просто транслятором одного кода в другой. Поэтому сейчас так популярны статические проверки и генерация кода за счет аннотаций специальных. Где-то это может небольшое неудобство, но оно приносит очень много пользы в перспективе. И null-safety как раз пример этого. Rust так вообще кардинально пошел по этому пути — там вся модель работы с памятью на аннотациях и статических проверках
                                        0
                                        Я согласен, null-safety — это хорошо. Но всему должна быть мера.
                                        Я конкретно про вышеупомянутый пример.
                                        0
                                        Smart cast to 'Int' is impossible, because 'value' is a mutable property that could have been changed by this time.

                                        Я считаю, это хорошая штука. Потому что язык должен быть предсказуемым. Поведение должно быть чётко специфицировано. Вот напишите соответствующую главу в спецификации языка: когда повторное чтение поля язык может считать безопасным, а когда нет. Написать в спецификации явно, что безопасен только такой фрагмент кода if(VAR == null) {return/throw ...} return VAR.xyz? А любой другой фрагмент опасен? Завтра кто-то пожалуется, что if(VAR == null) {return/throw ...} return 1+VAR.xyz не работает. Можно слева добавлять константу? А переменную? А другое поле? А волатильное поле? Напишите спецификацию и вы поймёте, что лучше запретить вообще все повторные чтения, чем описывать, когда они безопасны.

                                          0

                                          Можете привести определение безопасности, которому соответствует эта ошибка и пример кода, в котором она является небезопасной?
                                          Приведенный у вас код не имеет отношения к обсуждаемому.


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

                                        +15
                                        Сразу говорю, что Kotlin в жизни не видел, но очевидно, что вы здесь с позиции «привык, мне и так нормально», когда задача языка исправить то, что продолжает жить чисто как дань совместимости с С без учета того, что конструкция может быть лишь генератором ошибок без профита. Собственно, все новые языки нынче именно этим и занялись и очень успешно. И тут без знания Kotlin видно, зачем и почему все сделано.

                                        Истерично-бессмысленная война с null-абле

                                        Проблемы на границах с другим кодом это не причина отказываться не от модной, а необходимой вещи как null safety. В примере кода явно проблемы с диагностическим сообщением, не более — если компилятор умный, то ему должно быть понятно, что если условие не выполнено, то value всегда null и нужно выдавать ошибку. Изменить условие на value == null и ошибка должна исчезнуть. Если конечно котлин достаточно умен.

                                        Почему присваивание — это не выражение?

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

                                        Чем не угодил оператор "?:"?

                                        Тоже самое — оно того не стоит.

                                        За что убили автоматическое приведение типов?

                                        Здесь еще более явна причина — оно того не стоит. Помнится, Страуструп сам недоволен тем, насколько простителен С++ в этом плане, хотя он даже жестче С. Совсем неудивительно, что современные языки требуют только явных приведений.

                                        Недо-typedef

                                        В данном случае речь как раз о другом названии для типа и нужно это для рефакторинга, чтобы потом эти псевдонимы типов экспортировать, а не как привыкли в С, чтобы прятать реальные типы и давать им красивые имена без всякой реальной пользы. Недоделали может быть, но фича востребованная и есть во многих современных языках в том или ином виде.
                                          +2
                                          > > Чем не угодил оператор "?:"?
                                          > Тоже самое — оно того не стоит.

                                          А он то какие баги вносит?

                                            +2
                                            Нет, уже много раз говорилось, что тернарный оператор убрали за избыточность.
                                            if else может делать тоже самое в kotlin, большинство мест где тернарный оператор использовался в джаве это проверка на null и значения по умолчанию прекрасно заменяются .? и элвис оператором.
                                              +2

                                              Тернарный оператор вносит нечитабельный код. В больших проектах на C или Java с завидной переодичностью встречаются ... ? .... : .... выражения на 3 строки, а то и больше. И сделать с этим ничего нельзя — в отличие от if-else, ?-: даже с подсветкой синтаксиса плохо читается.

                                                +2
                                                Давайте не смешивать, нечитабельный код вносят программисты.

                                                Как выше заметили, if-else в Kotlin — это такое же выражение, т.е., в отличии от C и Java, тут тоже можно написать выражение на 3 строчки, которое будет плохо читаться. Да и тот же ReSharper тернарный оператор в if-else рефакторит в один клик. И из 3-х строчного выражения можно мгновенно получить простыню на 2 экрана, но читабельность это, честно говоря, не сильно улучшает.

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

                                                Впрочем, с программистами, не заботящимися о читабельности, это не самая большая проблема (вы видели что делают фанаты дженериков, например?).
                                                  –1

                                                  Программисты программистами, а некоторые синтаксические конструкции провоцируют писать плохой код.


                                                  В этом смысле if-else имеет преимущество. Во первых, выражение if-else чаще разносят на отдельные строки, почему то так принято. Во вторых, if-else легче подсветить ярким синим цветом который визуально выделяется в остальном коде, в то время как подсветка ? : такого эффекта не дает.


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

                                                    0
                                                    > Программисты программистами, а некоторые синтаксические конструкции провоцируют писать плохой код.

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

                                                    Звучит, как будто проблема вообще не в тернарном операторе, а в длинных выражениях. Запретите компилировать выражения, длиннее 40 символов (и применение магических чисел заодно), это решит проблему?

                                                    > Во первых, выражение if-else чаще разносят на отдельные строки, почему то так принято.

                                                    Ну «примите» не писать выражения в 3 строчки, будет не чаще.
                                                0
                                                Условный тернарный, возможно, потом сделают. Его сложно реализовать из-за того, что "?" и ":" — по отдельности тоже имеют смысл.

                                                https://discuss.kotlinlang.org/t/ternary-operator/2116/12
                                                  0

                                                  Да ну, то что там приведено в пример (двоеточие отдельно используется, ну надо же) — ересь какая-то. Вполне можно в разных правилах грамматики использовать одинаковые терминалы — главное аккуратно. Нет времени досконально разбирать грамматику Котлина, но думается, решить эту "мегапроблему" можно.

                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                +3

                                                Кратенько, по существу т.к. придирки и упражнения с схоластике не особо интересны.


                                                У for-in своя семантика, это не for из C.

                                                То что сейчас в Kotlin — это foreach, который именно в таком виде завели в каком-нить С# именно для того, чтобы обозначить его нишу.
                                                Мне, по сути, без разницы как оно называется.
                                                У меня претензия к отсутствию альтернативы нормального for. Считайте его хоть из С хоть из Java.


                                                Нет, чистый Kotlin null-safe, проверки будут только на стыке Java и Kotlin.

                                                Это неправда.
                                                Посмотрите генерируемый код.
                                                А на стыке Java\Kotlin как раз проверок не будет вообще, если на стороне Kotlin явно не указан "?"-й тип.


                                                Потому что в Java все потоки работают с одной кучей и в общем случае объект не привязан к какому-то отдельному потоку.

                                                В Java не нужно обеспечивать синхронизацию? Все операции\функции\блоки… атомарны? Компилятор как-то автоматически за меня определит границы блока синхронизации и создаст для них код?
                                                Если нет, то аргумент не принимается.


                                                ПС: Я не знаю языков где "объект привязан к какому-то потоку". Такие существуют?

                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                    +3
                                                    > для локальных переменных их можно было бы реализовать.
                                                    Но ведь smart cast и так работает для локальных переменных, в том числе мутабельных:
                                                    http://try.kotlinlang.org/#/UserProjects/vg26o1hplih1u2c89031fvdbl/svm0e1lu4fplliqtj8c5ejague
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0

                                                        Что исключительно логично!

                                                      0

                                                      И надо не забывать, что в Java проверка на null с выкидыванием NPE так чудно заоптимизирована, что и не стоит по сути ничего. Если смотреть на сгенерированный машинный код из JIT то там этих проверок и нету вовсе. Всё срабатывает через Uncommon Trap и прерывание процессора.

                                                      –5
                                                      Вам рассказать, как заменить цикл for на цикл while, или сами додумаетесь?
                                                        +3
                                                        То что сейчас в Kotlin — это foreach

                                                        for-loop в Kotlin это не совсем foreach. Вы уже наверное посмотрели byte-code и знаете, что в ряде случаев итератор в for-loop заменяется на стандартный цикл с индексами. Это ставит производительность for-loop на совсем другой уровень по сравнению в forEach.


                                                        Вообще я насчитал 5 случая применения С-подобного for-а, и считаю что Kotlin ни в чем не проигрывает:


                                                        1) Перебор значений:


                                                        for(x in 101..200) {...}

                                                        2) Перебор индексов в коллекции:


                                                        for(i in list.indices) {...}

                                                        3) Перебор элементов коллекции:


                                                        for(value in values) {...}

                                                        4) Повторение операции N раз:


                                                        repeat(N) {...}    // все оптимизируется в for(int i = 0; i < N; i++) {...}

                                                        5) Повторение операции пока верно условие


                                                        while(condition) {...}    
                                                        // да, для такого и в C лучше использовать while если хотите чтобы в коде было легко разобраться
                                                          –1
                                                          Вообще я насчитал 5 случая применения С-подобного for-а, и считаю что Kotlin ни в чем не проигрывает:
                                                          1) Перебор значений:

                                                          Ну, у вас же тут только тривиальный случай с простейшим условием остановки и единичным шагом, и с единственной переменной. В этом случае правда лучше перебор по диапазону. Но for можно куда более хитрый написать. Да, понятно, можно всё это while'ом сделать, но громоздко и некрасиво же будет. Тем более, реализация в компиляторе сишного (явского) фора — задача банальнее не придумаешь.

                                                        0
                                                        Нет, чистый Kotlin null-safe, проверки будут только на стыке Java и Kotlin.

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


                                                        Другое дело что на JVM null-проверки не только ничего не стоят, а более того, в ряде случаев и ускоряют код, так как позволяют JIT лучше оптимизировать.

                                                        +1
                                                        Чем не угодил оператор "?:"?

                                                        где такое еще есть?

                                                        В CoffeeScript. Там ? — оператор проверки на неопределённое или null-значение.


                                                        Унылые примеры

                                                        Тернарник:


                                                        // js
                                                        let a = some ? 23 : 42;
                                                        
                                                        // coffee
                                                        a = if some then 23 else 42

                                                        Проверка на неопределённое значение:


                                                        // js
                                                        if (a !== null && typeof a !== 'undefined') ...
                                                        
                                                        // coffee
                                                        if a? ...
                                                          +5
                                                          Откуда взялась эта абсолютно бредовая уверенность компилятора в том, что каждая буква моей программы — это элемент многопоточной конкуренции?


                                                          Дело не в многопоточности, а в том, что value — это не поле, а property. Когда вы вызываете if ( value != null ) или return value, вы обращаетесь не к полю value, а к геттеру, которого в вашем случае не видно, но он может появиться в наследнике класса. А так как геттер — обычная функция, он может вернуть как null так и не null, даже будучи вызваным 2 раза подряд.
                                                            +2
                                                            то с картами все значительно печальнее:


                                                            Можно вот так:

                                                            mapOf(
                                                                            "1" to 1,
                                                                            "2" to mapOf(
                                                                                    "2.1" to 21,
                                                                                    "2.2" to 22
                                                                            ),
                                                                            "3" to mapOf(
                                                                                    "3.1" to 31
                                                                            )
                                                                    )
                                                            
                                                              0

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

                                                                +2
                                                                То, что Вы хотите сделать, это:
                                                                    fun F() : Int {
                                                                        val result = value
                                                                        if ( result == null ) return 0
                                                                        return result
                                                                    }
                                                                


                                                                Или проще:

                                                                    fun F() : Int {
                                                                        return value ?: 0
                                                                    }
                                                                

                                                                В первом примере result уже не property, а локальная переменная(константа), значение которой мы проверяем напрямую, без геттера.
                                                                  0

                                                                  Я знаю несколько способов обойти описанную проблему, к описанному выше еще можно добавить "?.let", но вопрос был не об этом.
                                                                  Есть ли какие-то подтверждения того, что запрет на smart cast растет именно из "потенциальной неодинаковости getter-а", а не из-за многопоточности?
                                                                  Этот пример говорит об обратном:


                                                                  class A {
                                                                    private var value : Int? = 0
                                                                  
                                                                    fun F() : Int {
                                                                      if ( value == null ) return 0
                                                                      return value
                                                                    }
                                                                  }

                                                                  Ошибка точно та же, гетеров нет. То же и с "@JvmField".

                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                      –1

                                                                      В ссылках на то, что не имеет отношения к тексту есть какой-то смысл?
                                                                      Какой?

                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          –3

                                                                          Это только слова, к тому же без уточнения "non-private" еще и неправильные (и это без учета JvmField).
                                                                          Во всех случаях кроме наличия обоих get\set в классе будет поле.
                                                                          Я не помню, но вроде и с protected-ом будет protected поле, которое будет использоваться напрямую в родном классе и наследниках.


                                                                          Еще раз: зачем приводить бесполезные ссылки?
                                                                          Поверьте, я читал 99% документации, благо ее не вагон.
                                                                          Фактическое поведение компилятора сложнее 3х строчек описания

                                                                      0
                                                                      Оговорюсь сразу, на Котлине не пишу, но скорее всего вы не можете обратиться к полю value минуя геттер/сеттер от слова никак. Только в кастомном геттере/сеттере разве что.
                                                                +1
                                                                «Котлин не изучал, но никак не удержусь полить его тонной ховна, кажется, это модно».
                                                                А может стоит всё-таки сначала поизучать язык? Ах, хотя нет, зачем.

                                                                1) Банально запоролись уже на втором аргументе про null-safety. Ну и то, что кто-то привык к си — вообще не аргумент и кричать про бесполезность null-safety немного стрёмно. Как минимум это дополнительный инструмент построения более стабильной и прозрачной архитектуры.

                                                                image
                                                                И вышеприведенный код отлично компилируется и работает.
                                                                Другой вопрос в приведении типа к non-null — да, согласен насчет синтаксиса, код получается малость «экспрессивный».

                                                                2) Чем вам for-то так не угодил? Синтаксисом? Объёмом генерируемого кода? Посмотрите в сторону того как современные компиляторы оптимизируют код — в большинстве случаев ваш цикл превратится в обычный ваш «сишный» for.

                                                                3) Аннотации же нужны, декларирующие о null-ability типа! И по дефолту котлин вам разрешает использовать код из java без каста к non-null, но предупреждает, что вы можете выстрелить себе в ногу. Может всё-таки вы будете сначала искать хоть какую-то информацию перед написанием статей?
                                                                Это замечательно компилируется без каких-либо ошибок или предупреждений, запускается и с грохотом схлопытвается cо стандартным NullPointerException т.к. тут Kotlin не проверяет ничего и нигде. И где обеща..., тьфу, декларируемая безопасность?


                                                                4) Ну, наверное, за введение оператора "?:", чтобы избежать конфузов. Поясняю: в котлине есть оператор, который оценивает выражение слева, и если оно возвращает null, то отдаёт выражение справа. То, чего вам так не хватало в пункте 2 про null-safety.
                                                                Чем не угодил оператор "?:"?

                                                                За что ампутировали оператор "?:"?

                                                                Что усмотрели вредного в таких конструкциях?


                                                                5) Чтобы не было конфузов с boxing-ом типов? Скажу, что меня тоже бесит постоянно конвертить даблы во флоаты и обратно, но это плата за код, который работает именно так как мне надо и не выплёвывает сюрпризов.
                                                                За что убили автоматическое приведение типов?


                                                                6) Вам ничего не говорит пунктик про то, что котлин полностью обратносовместим с Java? Kotlin нигде не позиционируется как «офигенно крутой язык, меняющий jvm и все ещё компилирующийся в Java-совместимый байткод». Противоречие видите? Не под силу Kotlin изменить работу JVM и добавить туда ваши пресловутые шаблоны.

                                                                Правда, есть одна клёвая фишка — reified. Сродни тому, о чём вы так молите (о да, вы сможете скастить объект в ваш generic-type в рантайме!), работает правда только с inline методами (угадайте почему?).

                                                                В котлин-generics меня раздражает разве что in\out, особенно когда начинал осваивать язык, ловил разрыв шаблона.
                                                                Бог с ней с Java т.к. меня лично ее проблемы не волнуют. Меня волнует ущербность шаблонов конкретно в Kotlin, который позиционируется как другой язык, а не препроцессор для Java и, в результате, кивать на ее недостатки по меньшей мере бессмысленно.


                                                                7) А если вот так?
                                                                image
                                                                то с картами все значительно печальнее:

                                                                val tree = mapOf(
                                                                    Pair("dir1", mapOf(Pair("file1", 0), Pair("file2", 1))),
                                                                    Pair("dir2", mapOf(
                                                                      Pair("dir21", mapOf(Pair("file1", 0), Pair("file2", 1))),
                                                                      Pair("dir22", mapOf(Pair("file1", 0), Pair("file2", 1))))) )
                                                                



                                                                P.S. Вы бы ещё на отсутствие checked exceptions пожаловались.
                                                                  +1

                                                                  Меня просто поржает: с чего такая агрессивность-то?
                                                                  Наследие "кг\ам" или просто день не задался?
                                                                  Если я чем умудрился обидеть, то прошу прощения.


                                                                  В комменте, практически в каждом пункте текст, который вызван простым непониманием вопроса в статье.
                                                                  Не проблема, я не заставляю этот текст читать, тем более что, видимо, это противно.
                                                                  Это взаимно, так что на этом прощаюсь

                                                                    +7
                                                                    Эх, пофлеймить уже нельзя.
                                                                    Признаюсь — конкретно подгорело со стиля написания статьи, слишком много утвердительного тона и громких фраз, которые не очень коррелируют с реальностью.
                                                                    Если бы текст статьи был более «вопросительный» и мягкий, я бы и не отписался наверное не вспылил.

                                                                    Я понимаю, что статья написана после первых часов опыта работы с Kotlin и тут в основном задаются вопросы к сообществу, но эта форма подачи…
                                                                  –7
                                                                  После многих лет разработки под Android изучил Swift и в какой-то мере разработку под iOS на нем — без особых сложностей. Все это время присматривался к котлину. И решился на днях переписать маленький Android-проект с джавы на котлин. Через пару часов у меня нервы сдали и я забил.
                                                                  Вот вроде он на свифт похож очень, но что-то с ним не так, мне лично синтаксис и некоторые особенности (вроде синтаксических костылей вместо static методов и полей) совсем не понравились.

                                                                  А еще я не понимаю, почему каждый разработчик языка старается придумать что-то уникальное в синтаксисе, никак не влияющее ни на что, кроме создания доп. путаницы. Как я понимаю, Kotlin развивается с оглядкой на Swift, очень уж они похожи, но в одном языке fun, а в другом func. И куча таких мелких различий, которые при работе над двумя проектами на разных, но очень похожих языках неплохо ломают мозг.
                                                                    +3

                                                                    За пару часов язык в принципе освоить невозможно и, соответственно, делать о нем какие-то выводы сложно.


                                                                    Свифт, наверное, хорош (когда возникла необходимость писать под иос я, взглянув на синтаксис objective-C сказал что буду это делать только за очень отдельные деньги, и вопрос с тех пор не поднимался, жаль тогда не было свифта) но, к сожалению, за пределами одной платформы его не существует.
                                                                    Если же возникает задача для андроида, то, на мой взгляд, Kotlin — это наилучший выбор.
                                                                    Уж в сравнении с явой-то точно.

                                                                      +1
                                                                      За пару часов язык в принципе освоить невозможно и, соответственно, делать о нем какие-то выводы сложно.

                                                                      Само собой, освоить не получится. Но от свифта у меня были совсем другие впечатления за первые пару часов. Однако, это лишь мое субъективное мнение.

                                                                      когда возникла необходимость писать под иос я, взглянув на синтаксис objective-C сказал что буду это делать только за очень отдельные деньги, и вопрос с тех пор не поднимался, жаль тогда не было свифта

                                                                      +1, я года 4 собирался освоить iOS, каждый раз офигевал от синтаксиса Obj-C и бросал это дело.

                                                                      Если же возникает задача для андроида, то, на мой взгляд, Kotlin — это наилучший выбор.

                                                                      Вот не уверен — язык очень молодой, меняться же наверное будет. Тот же свифт уже вовсю в третьей версии, и каждый раз переход между мажорными версиями — это боль и жжение в области таза из-за того, что половину языка меняют :(
                                                                        +1

                                                                        Да, скорее всего, будут что-то менять.
                                                                        Я лично на это даже надеюсь :)


                                                                        Пока я каких-то проблем не испытывал.
                                                                        Вот сегодня (вчера) прилетела обнова языка и в ней оказались ампутированными битовые операции для типов Short и Byte. Пришлось править кусок либы и я уже хотел по этому поводу расстроиться, но потом посмотрел на реализацию и на Java и, в общем-то, причина этого действия стала ясна. Меня "убедили" в адекватности этого действия.
                                                                        Пока это было максимальное, на что я нарывался.


                                                                        Да и, на мой взгляд, движуха в языке не является особой проблемой.
                                                                        В любой развивающейся системе будут изменения — этого не избежать.
                                                                        Я лично пришел к следующему рецепту: при обновлении смотрю на ньюсы и, если количество решенных проблем или интересных фич перевешивает трудозатраты на адаптацию, то перехожу; если нет — сижу на старой версии.
                                                                        Это я по опыту изменений, которые приходилось вносить с принципиальным перелопачиванием многих сотен кб сорсов. В Kotlin-же я пока не видел ничего даже отданно схожего по трудозатратам.


                                                                        Я лично выбрал этот язык с прицелом именно на андроид т.к. остальные альтернативы мне показались значительно хуже либо синтаксически и стилистически (например Scala), или принципиально (например, нетипизированный Groovy).
                                                                        На мой взгляд, именно Kotlin максимально близок к Swift, но, возможно Вам лучше подойдет что-то другое т.к. иногда лучше перейти на что-то совсем другое, чем "очень похожее, но совсем не такое в мелочах" — будет меньше ошибок и расстройств.

                                                                          0
                                                                          Для андроида, на самом-то деле, вариантов не очень много. Джава да котлин. Котлин, как я полагаю, рано или поздно станет официальным языком для ведра, заменив джаву. По крайней мере, это было бы логично.
                                                                          Java сейчас официальный язык.
                                                                          Остальное — уже как-то совсем не тру. Понятно, что все эти языки компилятся в байт-код джавы и на выходе получаем примерно одно и то же, но как-то неправильно это, прямо как кроссплатформенные аппы на вебвью делать :) К тому же, у скалы и груви, насколько я помню, предназначение другое.

                                                                          Меня-то в принципе и джава почти устраивает. По сравнению с Xcode + Swift, Android Studio + Java хотя бы адски не лагает на топовом железе. Я все на старенький макбук грешил, а оно точно так же умудряется лагать на i7-7700K с 32 гигами DDR4-2400… Причем, говорят, Obj-C не лагает, лаги именно в автокомплите свифта :)

                                                                          В любом случае, я бы не стал крупные проекты делать на молодых языках. Это все очень круто и ощущения отличные, но потом язык кардинально меняется со следующим релизом, и становится очень больно :)
                                                                            +2

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


                                                                            Насчет смены жавы — я просто не верю.
                                                                            Причина проста: никто не будет переписывать существующий код. Во-первых, потому что в принципе не будут (текущее лоскутное одеяло АПИ тому подтверждение) и, во-вторых, потому что без баквард-компатибилити гугл потеряет огромное количество "лайков". Поддержать же два языка бессмысленно. Поэтому я думаю, что Java в андроиде навечно.


                                                                            Насчет крупных проектов — это философский вопрос, на самом деле.
                                                                            К примеру я, не так уж и давно, перестал писать вполне себе крупный проект на языке С, который собирался таким зверем как Watcom 8.0, про который многие уже и не знают, наверное.
                                                                            На работе народ вполне себе непрерывно пишет на Borland C 3.0.
                                                                            Собственно, в самой старости языка нет никакой проблемы с точки зрения поддержки проекта, если функционал этого проекта состоялся.

                                                                              0
                                                                              Насчет смены жавы — я просто не верю.
                                                                              Причина проста: никто не будет переписывать существующий код

                                                                              Сложно сказать, что там у гугла на уме — но Kotlin ведь полностью совместим с джавой. Достаточно просто рантайм принести на девайс, дабы не тащить его в каждой апк-шке.

                                                                              Apple ведь точно этим сейчас и занимается — меняет Obj-C на Swift. И даже частично компоненты переписывает — говорят, в нынешней макоси как минимум Dock уже на свифте написан :)
                                                                                –8

                                                                                Любой JVM язык полностью совместим с жавой. По определению :)
                                                                                Вообще, ассемблер JVM крайне примитивен и, если бы в него, в свое время, конструктивно не были бы воткнуты всякие "хаки" для ускорения, то для него можно было бы собрать вобще любой язык, с любыми произвольными возможностями. Т.к. основной задачей любого JVM языка является совсем не поддержка этого ассемблера, а обеспечение точного и однозначного вызова java-языкХ-java, то все они абсолютно одинаково с ней "совместимы": генерируют точно такие же классы, неотличимые в среде выполнения от сгенерированных жавой.


                                                                                С "доложить рантайм" понятно, но стимула делать это у гугла я пока не вижу. С апле ситуация другая совсем. У них крошечный процент рынка мобильной техники, несравнимо более высокий порог входа для разработчика, стоимость самой разработки и нулевой интерес аппартных производителей к их платформе. Swift — это вынужденный шаг по популяризации свой платформы при сохранении закнутого статус-кво. Насколько удачный сказать сложно, но без него апле бы попросту захирел и в мобильной области, как уже случилось с настольной.

                                                                                  0
                                                                                  С «доложить рантайм» понятно, но стимула делать это у гугла я пока не вижу.

                                                                                  В принципе, да, тут тяжеловато с точки зрения обратной совместимости — Apple-то свои девайсы очень долго обновляет, и им не проблема принести новые либы. А с Android-вендорами все совсем не так однозначно. Разве что нести рантайм в апк для старых версий ОС, а для новых не нести… Но это куча геморроя.

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

                                                                                  Да вроде как за последние 4 года у macOS доля аж на 2.5 процента выросла (до 9.6%), что очень немало для их ценовой категории. Так что пока живее всех живых :)
                                                                                    0

                                                                                    Мы же вроде про мобилки, а там iOS. МacOS это "гуй на линуксе" или я все перепутал?
                                                                                    Впрочем, бог с ними.
                                                                                    Любые рассуждения на тему могут\будут\почему все равно голимые домыслы, которые еще и к теме статьи отношения не имеют :)

                                                                                    +1
                                                                                    > Любой JVM язык полностью совместим с жавой
                                                                                    Это не правда. Java != JVM.
                                                                                    К тому же не забывайте что совместимость работает (или не работает) в обе стороны, из Java вы можете использовать любой Kotlin код, что не правда для многих других JVM языков, порой очень сложно или невозможно вызвать код написаный на другом JVM языке.
                                                                                    Поддерживать полную двухстороннюю совместимость с Java это совсем не легкая задача
                                                                                      0

                                                                                      Т.е. существуют языки, в которых невозможно устаномить listener для класса на Java?
                                                                                      Если это так, то спасибо, не догадывался.
                                                                                      А какая ниша применения у такого рода языков?

                                                                                        +2

                                                                                        Обычно требуется ограничить себя в некоторых фичах, чтобы сделать Java-совместимое API или callback. Например, многие Scala-библиотеки имеют нативное API и дополнительно Java API, частично пересекающееся с нативным.

                                                                                          –2

                                                                                          Получается, что то что я написал выше: java-языкХ-java — это вполне правильно.
                                                                                          Языков JVM, которые бы не позволяли вызвать ява-код и быть вызванными из него не существует.
                                                                                          А то что типы могут потеряться и и тому подобное — это уже не принципиальные особенности т.к. они логически обоснованы разными языками.

                                                                                          +2

                                                                                          Нет, listener установить можно всегда если можно унаследоваться от нужного типа. Речь шла о ситуации, когда код на другом языке уже написан — но из Java нельзя его вызвать, потому что компилятор Java не понимает что от него хотят.


                                                                                          Языков не скажу, но как так можно сделать могу предположить. К примеру, JVM, в отличии от Java, поддерживает перегрузку методов по возвращаемому значению. Если сделать на другом языке такие методы — то из Java нельзя будет их вызвать.


                                                                                          Или, например, можно использовать ключевые слова в качестве идентификаторов.


                                                                                          Или вот пример из соседней оперы. На языке C++/CLI можно создать публичный шаблонный класс:


                                                                                          template <typename T> public ref struct Holder {
                                                                                              T value;
                                                                                          };
                                                                                          
                                                                                          public ref struct Example {
                                                                                              static Holder<int>^ Foo() {
                                                                                                  Holder<int>^ result = gcnew Holder<int>();
                                                                                                  result->value = 42;
                                                                                                  return result;
                                                                                              }
                                                                                          };

                                                                                          Теперь если добавить такую сборку в проект на C#, то результат вызова Example.Foo() нельзя записать в поле класса или в явно типизированную переменную! Кажется, в D похожий эффект назвали "Волдеморт-типом", типом-который-нельзя-назвать. Почему? Да потому что класс называется Holder<int>. Прямо так, с угловыми скобками. И это не generic, это обычный класс :)


                                                                                          Тот же ILSpy декомпилирует этот класс в C# вот так:


                                                                                          public class Holder<int>
                                                                                          {
                                                                                              public int value;
                                                                                          }

                                                                                          Подозреваю, что в том же упоминавшемся выше Ceylon сделали что-то подобное...

                                                                                            0
                                                                                            К примеру, JVM, в отличии от Java, поддерживает перегрузку методов по возвращаемому значению.

                                                                                            Приятно узнать что-то интересное, спасибо. Погляжу в jvms.


                                                                                            Из эпичных вещей с неработающим interop: из Scala нельзя было вызвать валидный java-код. Проблема осталась и в 2.12.1.


                                                                                            Код логгера со стороны java (slf4j) имеет несколько перегруженных методов (для пример достаточно двух: Logger#info(String, Object...) и Logger#info(String, Object, Object).


                                                                                            Из scala вызов такого метода с тремя аргументами аргументами типа String становится ошибкой компиляции, а из java вполне допустим, т. к. у них разные правила обработки перегруженных методов. Под спойлером воспроизводимый пример.


                                                                                            пример

                                                                                            Возьмем для примера такую имплементацию "логгера":


                                                                                            class Logger {
                                                                                              void info(String s, Object... args) {
                                                                                                System.out.println(s);
                                                                                                System.out.println(args.getClass().getName());
                                                                                              }
                                                                                              void info(String s, Object a1, Object a2) {
                                                                                                System.out.println(s);
                                                                                                System.out.println(a1.getClass().getName());
                                                                                                System.out.println(a2.getClass().getName());
                                                                                              }
                                                                                              public static void main(String[] args) {
                                                                                                new Logger().info("one", "two", "three");
                                                                                              }
                                                                                            }

                                                                                            Она компилируется и работает (javac Logger.java, java Logger, если хочется проверить).


                                                                                            Теперь попробуем её вызывать из scala:


                                                                                            object Main {
                                                                                              def main(args: Array[String) = {
                                                                                                val l = new Logger
                                                                                                l.info("one", "two", "three")
                                                                                              }
                                                                                            }

                                                                                            И при вызове scalac Main.scala получаем


                                                                                            Main.scala:4: error: ambiguous reference to overloaded definition,
                                                                                            both method info in class Logger of type (x$1: String, x$2: Any, x$3: Any)Unit
                                                                                            and  method info in class Logger of type (x$1: String, x$2: Object*)Unit
                                                                                            match argument types (String,String,String)
                                                                                                l.info("one", "two", "three")
                                                                                                  ^
                                                                                            one error found
                                                                                              0
                                                                                              Из scala вызов такого метода с тремя аргументами аргументами типа String становится ошибкой компиляции

                                                                                              Во!
                                                                                              В Kotlin точно так же.
                                                                                              Я пытался написать плагин к JavaDOC и вызвать явовский парсер из Kotlin не смог вообще. Именно из-за обилия перегруженных функций с одинаковыми типами параметров (куча форм вызова main с передачей ей строковых параметров).

                                                                                                0

                                                                                                Так почему не смогли? Он разве не точно так же из Java вызывается?

                                                                                            +1
                                                                                            > невозможно устаномить listener
                                                                                            Там огромное количество эдж кейсов, вроде листенер то установить вы сможете, а вот результирующее значение будет с non-reified generic или еще какими то ограничеями из-за того, что это Java объект, а не объект вашего JVM языка.
                                                                                            Если интересно можете посмотреть какие ограничения на фичи накладывает Scala или Closure при вызове из Java и наоборот.
                                                                                +4
                                                                                > Вот не уверен — язык очень молодой, меняться же наверное будет.
                                                                                У Kotlin совершенно другой подход по отношению к обратное совместимости https://blog.jetbrains.com/kotlin/2015/12/kotlin-1-0-beta-4-is-out/#comment-41068
                                                                                Так же под рукой нет, но есть посты об этом в официальном блоге Kotlin

                                                                                Они не в том положении как Apple, которые могут нагнуть все комьюнити и сломать обратную совместимость очередной версий и даже существующий код заставить переписать, иначе в AppStore не пустят (привет Swift 3)

                                                                                И Kotlin не намного моложе Swift: Разработка Swift внутри Apple начата в Июле 2010-го, первый коммит в репозиторий Kotlin был сделан в Октябре 2011 (возможно разарботка была и раньше). В том же 2011 впервые анонсирован, Switft показали публике в Июне 2014, тут еще большой вопрос кто у кого что подсмотрел
                                                                                  0
                                                                                  del
                                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                                  +2

                                                                                  Описание в обратной нотации, т.е. "имя-тип" диктуется не огладкой на Pascal или тенденциями, а простым требованием синтаксиса там, где описние типа опционально. В противном случае текст программы читается (и возможно компилируется) неоднозначно.
                                                                                  Фигурные же скобки — это просто всего один из трех возможных на клавиатуре вариантов самой краткой записи начала и конца. Квадратные как-то "сроднились" с индексацией, вот и получается что выбрать производителю просто не из чего. Кому хочется больше альтернативщины, те используют круглые, кому что попроще — фигурные :)

                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                    +1
                                                                                    использовать ключевые слова для объявления функций и переменных, как в Pascal

                                                                                    Почему не сказать сразу "как в ML" (истоки и того, и другого лежат в 70х)? То же ключевое слово fun было в Standard ML, как минимум.

                                                                                      0
                                                                                      > Почему не сказать сразу «как в ML»

                                                                                      Напомнило, на какой-то конференции MS Баллмер отвечал на вопросы из зала. Слышно было не очень.

                                                                                      — Будет ли развитие у Windown Home Server?
                                                                                      — (прислушиваясь) «Windows what?»
                                                                                      — Exactly.
                                                                                  +15

                                                                                  Я понимаю, что писать возмущенные тексты приятно :) Не могу сказать того же о чтении таких текстов. Постараюсь по существу ответить на ту часть, которая не покрывается документацией.


                                                                                  Про тернарный оператор (?:): многим не нравится, мы посмотрим, что можно сделать.
                                                                                  Про литералы для коллекций: тоже сделаем.
                                                                                  Объявления новых типов, а не алиасов тоже сделаем.


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


                                                                                  C++ для JVM не сделаем. У нас другие задачи :)

                                                                                    +6
                                                                                    Пожалуйста учитывайте, что многим нравится if-else в текущем виде и не нравится тернарный оператор. То же про отсутствие C-шного for-цикла. Мне Kotlin нравится в том числе тем, что в него не насовали всего, чтобы всем угодить, а постарались по мере возможности обойтись меньшим числом конструкций. Это я не к тому, что не надо ничего добавлять, конечно.
                                                                                      –4
                                                                                      Пожалуйста учитывайте, что многим нравится if-else в текущем виде

                                                                                      Так его убирать и не предлагают, вроде бы.


                                                                                      и не нравится тернарный оператор

                                                                                      (удивлённо) — так и не используйте, раз не нравится.


                                                                                      То же про отсутствие C-шного for-цикла.

                                                                                      То же про отсутствие C-шного for-цикла. :) ну, правда, ни кто же не заставляет.


                                                                                      не насовали всего, чтобы всем угодить,

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

                                                                                      –2

                                                                                      Огромное спасибо за отклик.
                                                                                      Рад что одиозное название не отпугнуло от чтения
                                                                                      В данном случае, как в аббревиатуре RTFM, ругательная часть просто является обозначением, а не моим отношением :)


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


                                                                                      Пользуясь случаем хотелось бы обратить внимание на такие возможности:


                                                                                      1. Добавить компилятору ключи командной строки, которые бы могли влиять на его поведение. В настоящее время ключей практически нет, но их использовани могло бы решить некоторые частные вопросы. Например ключ для отключения механизма null-safety был бы часто полезен.
                                                                                      2. Во многих языках есть удобная фича: возможность указать ключи (или настройки компиятора) прямо в исходных текстах. Например в С для этого существуют pragma. Использование таких конструкций позволяет локально менять настройки и действует на весь модуль что в яве аннотациями не сделать, насколько я понимаю.

                                                                                      В результате этих двух пунктов можно было бы вынести какой-то код в отдельное место и указать для него другие правила (например отключить тот же null-safety, который при работе с каким-нить свингом просто мозг выносит т.к. превращает исходник в забор из "!").

                                                                                        0
                                                                                        ключ для отключения механизма null-safety был бы часто полезен

                                                                                        Может я не правильно вас понял, но ведь есть ключ для отключения генерации not-null проверок


                                                                                        $ kotlinc -X
                                                                                        Usage: kotlinc-jvm <options> <source files>
                                                                                        where advanced options include:
                                                                                          -Xno-call-assertions       Don't generate not-null assertion after each invocation of method returning not-null
                                                                                          -Xno-param-assertions      Don't generate not-null assertions on parameters of methods accessible from Java
                                                                                          –3

                                                                                          Поняли правильно, но я там дальше написал использование.
                                                                                          Это не то.


                                                                                          1. Это глобальный ключ.
                                                                                            Я не хочу отключать проверки во всем проекте, без возможности указать его действие для модуля он для меня бесполезен.
                                                                                            Можно указать раздельные ключи индивидуально для каждого файла в IntelliJ?


                                                                                          2. Насколько я понял этот ключ делает не то что я хочу.
                                                                                            Он убирает оверхед проверок, а мне нужно другое: а) отключить исключение из smart-cast для var и б) временно выключить ошибки преобразования, т.е. временно интерпретировать типы не как "?", а как "!", что позволит мне оперировать nullable без огорода "!!" в коде.
                                                                                          +2
                                                                                          Пользуясь случаем хотелось бы обратить внимание на такие возможности:

                                                                                          Вводить такие ключи и прагмы — это крайняя мера, мы пока попробуем без них. (Аннотации на файл в Котлине есть, но их для таких целей мы использовать пока не хотим.)


                                                                                          В данном случае, как в аббревиатуре RTFM, ругательная часть просто является обозначением, а не моим отношением :)

                                                                                          Неприятнее всего было, конечно, не название, а то отношение к нам, которое выражено в тексте.

                                                                                        –6
                                                                                        Серьезно? Они предлагают для массивов arrayOf вместо нормального [...]? Даже в пыхе это лет пять как прикрутили.
                                                                                          +1
                                                                                          Зато научитесь понимать отличать Array<Array<Array<Array<Double>>>> и Array<Array<Array<Double>>> с первого взгляда и с любого расстояния

                                                                                          Кстати прекрасный кейс где typealias отлично работают:


                                                                                          typealias Matrix4<T> = Array<Array<Array<Array<T>>>>
                                                                                          typealias Matrix3<T> = Array<Array<Array<T>>>
                                                                                            +2

                                                                                            Э… вообще-то у матрицы ранг всегда 2 и она обязана быть выровненной. А то, что вы привели, даже как назвать-то и не знаю...

                                                                                              0
                                                                                              Рваный массив
                                                                                                0

                                                                                                Нет, рваный массив — синоним разреженного, а не невыровненного массивы 4го ранга. Но, в любом случае, проблема не в том, как описать это на русском — а в том чтобы придумать имя типа на английском :)

                                                                                                  –1
                                                                                                  Нет, я имел в виду именно такой:
                                                                                                  [[0, 0, 0],
                                                                                                  [0, 0],
                                                                                                  [0, 0, 0, 0, 0]]
                                                                                                0

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

                                                                                                  0
                                                                                                  Может так?
                                                                                                  Matrix3d, Matrix4d
                                                                                                  
                                                                                                    0

                                                                                                    Увы, но название "Matrix3d" уже зарезервировано под квадратную матрицу 4x4… :)

                                                                                                      0
                                                                                                      сколько я видел, квадратные матрицы всегда называются просто MatrixN, потому что постфикс `d` обозначает измерение. В такой нотации матрица 4х4 должна называться Matrix4 и это звучит вполне логично. Можно посмотреть например на opengl
                                                                                                        0

                                                                                                        google://matrix3d


                                                                                                        The matrix3d() CSS function describes a 3D transform as a 4x4 homogeneous matrix. The 16 parameters are described in the column-major order.

                                                                                                        Своя логика тут есть, потому что матрица 4х4 описывает проективное преобразование трехмерного объекта.

                                                                                                          0
                                                                                                          осталось понять, почему матрицу трансформации не назвали матрицей трансформации, а назвали matrix3d :-)
                                                                                                            0

                                                                                                            Потому что слово "трансформация" уже используется в имени свойства. Зачем дублировать?


                                                                                                            А вот отличать проективное преобразование в пространстве от преобразования на плоскости — надо!

                                                                                                              0
                                                                                                              ну, да, в контексте другой функции, для простоты, чтобы различить от такой же на плоскости… это всё не про «правильное» название
                                                                                                          0
                                                                                                          потому что постфикс d обозначает измерение

                                                                                                          Конкретно в OpenGL постфикс d означает double. Там ещё есть постфиксы f и i.

                                                                                                        0
                                                                                                        Array3D, Array4D
                                                                                                      +1

                                                                                                      Вообще-то, ранг матрицы не обязан равняться двум. То есть может, конечно, если в матрице два линейно-независимых столбца (строки), но это совершенно не обязательное условие существования матрицы.

                                                                                                        0

                                                                                                        Уточнение: когда массив — матрица, его ранг всегда 2. Ранг массива и ранг матрицы — это разные понятия...

                                                                                                          +1

                                                                                                          Так немного лучше, хотя "ранг массива" в русском языке называется размерностью массива :)

                                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                      +5
                                                                                                      null-абле

                                                                                                      Это слово раздвинуло мое сознание. Почему не "nullable"?


                                                                                                      тред, трид

                                                                                                      Поток


                                                                                                      while-ом

                                                                                                      Просто "while", слово на латинице в русском тексте не склоняется.

                                                                                                        0
                                                                                                        Не претендую даже на звание профана в Kotlin, но, на мой дилетантский взгляд, вот на этом участке кода:
                                                                                                        var value : Int? = null
                                                                                                        
                                                                                                        fun F() : Int {
                                                                                                          if (value == null) return 0
                                                                                                          return when (Random().nextInt()) {
                                                                                                            3    -> value!! + 2
                                                                                                            12   -> value!! + 1
                                                                                                            5    -> value!! * 4
                                                                                                            else -> 0
                                                                                                          }
                                                                                                        }
                                                                                                        

                                                                                                        лучше таки использовать встроенные иснтрументы обеспечения null-safety:
                                                                                                        var value: Int? = null
                                                                                                        
                                                                                                        fun F() = value?.let {
                                                                                                                when (Random().nextInt()) {
                                                                                                                    3 -> it + 2
                                                                                                                    12 -> it + 1
                                                                                                                    5 -> it * 4
                                                                                                                    else -> 0
                                                                                                                }
                                                                                                            } ?: 0
                                                                                                        
                                                                                                          –1

                                                                                                          Это всего лишь кратенький пример иллюстрация.
                                                                                                          Обойти в нем использование "!!" можно несколькими способами.
                                                                                                          Но в реальной жизни, к сожалению, не все они удобны или применимы.
                                                                                                          К примеру, при работе с интерфейсом swing в одной функции может происходить общение с кучей элементов интерфейса, каждый из которых nullable и с ним производится всего 1-2 действия.
                                                                                                          Ни оборачивать каждый элемент в лямбду, ни заводить локальную переменную для них не имеет никакого смысла — это просто хуже читаетя и занимает больше места.
                                                                                                          Приходится городить кучи "!!" в местах, которые гарантированно не null при использовании.
                                                                                                          Это достает, вплоть до написание глобальной аннотации к классу.

                                                                                                            0

                                                                                                            Не уверен про ваш случай с Swing, но как я понял это близко к ситуации у нас в Android при работе с UI и вьюхами. Мы убираем nullability через lazy проперти, так же вы можете использовать lateinit

                                                                                                              0

                                                                                                              Да, аналогично, но в андроиде ее практически нет.


                                                                                                              Во-первых, у котлина есть автоматическое создание доступа к элементам описанным в лайауте (т.е. создавать переменные в классе вообще не нужно).


                                                                                                              Во-вторых, в андроиде есть дизайнер.
                                                                                                              В свифте дизайнер(ы) напоминают "жалкое подобие левой руки" и, как правило, весь инерфейс создается в рантайме полностью вручную.


                                                                                                              В-третих, из-за наличия дизайнера, в коде под андроид вы оперируете УЖЕ созданным интерфейсом и, соответственно, в активити\фрейме… можно использовать любой способ доступа к элементам (в том числе вручную lazy, если вас устраивает безумный оверхед на классах для каждой анонимной функции). При ручном создании интерфейса этим "лейзам" просто неоткуда брать компонент, поэтому используются всякие аналоги вида "firstAssign", но их не всегда удобно использовать.

                                                                                                                0

                                                                                                                Ну про дизайнер вы зря, за 5 лет в Android никогда не пользовался визуальным дизайнером кроме как превью.
                                                                                                                А почему не подходит lateinit?

                                                                                                            0
                                                                                                            Не претендую даже на звание профана… но, на мой дилетантский взгляд
                                                                                                            профан, насколько можно верить словарю ушакова — это значит «Человек, совершенно несведущий в чем-нибудь», однако дилетант — это " Занимающийся [чем-либо] как любитель (не профессионально)". Получается, что дилетант несколько более сведущ в предмете, чем профан. Сори за занудство, просто мне самому бывает очень неприятно, когда я понимал какое-то слово неправильно и употреблял, а мне про это даже никто не сказал.
                                                                                                            –1
                                                                                                            ПС: В виде отдельного гвоздя в голову я пожелаю кому-нибудь написать библиотеку для работы с матрицами. Зато научитесь понимать отличать Array<Array<Array<Array>>> и Array<Array<Array>> с первого взгляда и с любого расстояния.


                                                                                                            typealias Matrix4D<Double> = Array<Array<Array<Array<Double>>>>
                                                                                                            typealias Matrix3D<Double> = Array<Array<Array<Double>>>
                                                                                                            
                                                                                                              –1
                                                                                                              Не то, что бы я критикую Kotlin, но положение, когда его создатели находятся все время в процессе понимания, что надо в язык добавить, а что убавить — не является обнадеживающим для его перспектив. А если еще и обратная совместимость для такого молодого и не претендующего на громкую славу ЯП будет постоянно ломаться, то это вообще.
                                                                                                              В разработке ЯП важен баланс инженерного (частного) и академического (системного) подходов. Kotlin повело в инженерную крайность.
                                                                                                                0
                                                                                                                А если еще и обратная совместимость <...> будет постоянно ломаться

                                                                                                                Надеюсь тема про совместимость взялась не из довольно тухлого срача в VK несколько-дневной давности, потому что Kotlin еще ни разу (тьфу-тьфу) не нарушил совместимость, которую команда обещала.


                                                                                                                Процитирую более развернутый комментарий @gilgor: https://habrahabr.ru/post/322256/#comment_10081594


                                                                                                                У Kotlin совершенно другой подход по отношению к обратное совместимости https://blog.jetbrains.com/kotlin/2015/12/kotlin-1-0-beta-4-is-out/#comment-41068
                                                                                                                  +1
                                                                                                                  Спасибо, пропустил. Будем надеяться, что у них получится!

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

                                                                                                              Самое читаемое