Kotlin: две ложки дегтя в бочке меда

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



    Kotlin был с воодушевлением воспринят сообществом, так как новый язык программирования значительно упрощает написание кода, заменяя собой Java. Особенно это заметно на Android, где новые версии Java появляются, мягко говоря, «со скрипом». Многие аппараты обновляются с большими задержками, ещё больше – вообще не получают обновления прошивки. В лучшем случае поддержка аппарата заканчивается примерно через 2 года после его выпуска,  что сулит одно, максимум — два обновления системы. Однако люди продолжают пользоваться устройствами, а значит разработчикам приходится ориентироваться как на (пока ещё) новейший Android 8, так и на Android 5, а то и более ранние версии, где уж точно нет не только самой последней, но даже актуальной реализации Java. Для справки, сейчас суммарная доля Android 7 и 8 — тех версий, где есть поддержка Java 8 со стороны системных библиотек — составляет 42,9%, всё остальное — Java 7.

    Кто-то может говорить, что Java устарела. Но есть также и мнение, что Java – это язык, который хорош «as is», и его не нужно трогать. Это как НОД (наибольший общий делитель) в математике. Он содержит джентльменский набор функций, а для тех, кому нужны дополнительные возможности сегодня доступны специальные языки, работающие уже поверх JVM. Этих языков уже расплодилось довольно много: Scala, Clojure, JRuby, Jython, Groovy и прочие, менее известные. Однако Kotlin отличается рядом плюсов. Этот язык оставляет больше свободы программисту: он позволяет одновременно использовать готовые фрагменты на Java, объединяя старый и новый код.

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

    Скрыть нельзя показать?


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

    Напомню, что в Java есть 4 разных категории, которые позволяют разграничить видимость. Для классов их всего два – они видны либо в рамках пакета (package private), либо полностью открыты (public). Но метод или поле уже можно сделать private (он будет недоступен вне класса), видимым только для пакета (package private), можно сделать так, чтобы метод дополнительно был виден наследникам, как в своём пакете, так и вне пакета (protected), а также можно его сделать видимым для всех (public).

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

    Одним словом, опций в Java — более чем достаточно. А вот в Kotlin почему-то ограничились тем, что ввели публичную и приватную видимость, а также видимость для наследников. Кроме этого, правда, они ввели разграничение по модулям, но разграничить доступ к методам и классам по пакетам стало нельзя. Модуль — это уже не конструкция самого языка, и частенько люди понимают под этим термином довольно-таки разные вещи. В Kotlin официально определяют модуль как «набор скомпилированных вместе Kotlin-файлов».

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

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

    Тут сразу хочется вспомнить про пакеты, ведь в Kotlin эта сущность никуда не делась, но на видимость она, увы, не влияет. Конечно, всегда можно наделать больше модулей, но, учитывая  особенности Gradle, это просто нерационально: скорость сборки будет снижаться. Да, есть возможность засовывать классы в один файл, но в случае с большим проектом файл станет действительно «тяжёлым». Поэтому хотелось бы получить какой-то другой способ сокрытия методов, например, по образцу Java.

    Не обрабатывай это… даже если и надо бы


    Второй — довольно спорный (потому что некоторые считают их использование моветоном), но тем не менее минус – отсутствие проверяемых исключений. Эти средства есть в Java, но в Kotlin решили их не реализовывать. В официальной документации есть пример про интерфейс Appendable. К Appendable можно присоединять строки, и поскольку строки могут подсоединяться к объектам, связанным с вводом/выводом, например, при записи в файла или в сеть, при вызове методов интерфейса потенциально можно получить IOException. И такие случаи делают использование проверяемых исключений неудобным.

    Объясняют свою аргументацию создатели языка следующим образом: если мы используем StringBuilder, обращаясь к нему через Appendable, нам, оказывается, нужно обязательно обрабатывать IOException, даже если вы уверены, что оно в принципе не может произойти:

    Appendable log = new StringBuilder();
    try {
        log.append(message);
    } catch (IOException e) {
        // Must be safe
    }

    Выход из ситуации: исключение «ловят», но ничего с ним не делают, что, разумеется, не есть хорошо. Однако возникает вопрос: если мы заведомо работаем со StringBuilder через интерфейс Appendable – почему не взаимодействовать напрямую со StringBuilder? Тут есть важная разница: при работе с Appendable, если мы не знаем, какая под ним лежит конкретная реализация, исключение ввода/вывода становится действительно возможным, но вот StringBuilder точно его не выдаст, и соответствующие методы в нём так и объявлены, хотя они и реализуют интерфейс. Так что пример получается довольно натянутый…

    Особенно интересно, что авторы документации ссылаются на главу 77 в Effective Java, где говорится, что исключения нельзя ловить и игнорировать. Но вот только в соседних главах пишется, что проверяемые исключения использовать можно и нужно, если делать это с умом. Если так избирательно цитировать, можно оправдать любую точку зрения.

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

    Если обратиться с вопросом к разработчикам, они просто разводят руками и говорят, что в других языках нет проверяемых исключений и ничего, на них тоже создаются большие и надёжные программы. Но они и на откровенно неудачных языках успешно пишутся, это ведь не аргумент. На StackOverflow на подобный вопрос говорят, что «нужно читать документацию» — хороший ответ, очень удобный в подобных ситуациях. Только вот документации может не быть, она может быть неполной или устаревшей — компилятор же её не проверяет. Наконец, в ней можно так и не найти ответ.

    Да, это правда, что наличие проверяемых исключений может приводить к созданию неудобных API. Когда проверка на исключения — заведомо излишняя, чтобы компилятор «был доволен», приходится прописывать try...catch и фактически просто создавать мусорный код, ведь при обработке этих исключений не делается ничего. И то, что в функциональном коде неудобно их использовать — тоже правда. Но ведь проверяемые исключения — это лишь инструмент, который точно так же можно использовать грамотно, там, где это нужно.

    Непонятно, почему язык забрал эту возможность, если философия Kotlin заключается в доверии большего количества инструментов программисту (по крайней мере, нам так показалось), и если автор кода может грамотно расписать, где какие исключения будут, почему бы не поверить ему? Взять те же перегружаемые операторы, которые убрали из Java, потому что они могут привести к появлению API с неочевидными действиями — это была защита программистов от самих себя. В Kotlin, напротив, есть возможность перегружать операторы и делать многие другие вещи – так почему же нет проверяемых исключений? Уж не сделали ли это создатели из-за того, чтобы просто было не как в Java?

    Мы ждём перемен…


    Максимум, на что мы можем рассчитывать под Android – это Java 7 или Java 8 (но уже с некоторыми ограничениями и оговорками), в то время как на подходе уже Java 11. С использованием Kotlin программирование на Android становится намного проще, сокращается количество строк текста.

    Остаётся лишь надеяться, что разработчики дополнят этот очень полезный язык теми возможностями, которые по неочевидным причинам отсутствуют сегодня. Может быть, в следующих версиях появятся новые инструменты для анализа исключений в IDE, а также новые категории приватности. Но, судя по всему, это дело в лучшем случае отдалённого будущего, так как никаких обещаний разработчики языка пока даже не озвучивали.
    Acronis
    135,00
    Компания
    Поделиться публикацией

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

      +3
      философия Kotlin заключается в доверии большего количества инструментов программисту (по крайней мере, нам так показалось)

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


      Собственно, почему вам не хватает internal? Да, это область видимости чуть больше, чем package-private, но и проблем с ним меньше.


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


      Как мне показалось из статьи, у Вас мозг "отформатирован" под Java и ещё не до конца осознал Kotlin, это всё-таки не просто Java-с-плюшками, это другой язык, хоть у него и хороший интероп с экосистемой Java.

        0
        Kotlin создавался как удобный, прагматичный язык
        А есть серьёзные языки, которые создаются как неудобные, что ли? Проблема в том, что принцип доверия-недоверия разработчику объективен, а удобство — это уже понятие субъективное. Можно либо руководствоваться какими-то принципами при принятие решений при разработке языка, либо нет. Удобство — это не принцип.

        область видимости чуть больше
        Если в каждом модуле пара пакетов, тогда «чуть». А если пакетов больше, то тогда уже не «чуть».

        это другой язык
        В этом другом языке есть проблема, которую я чётко обозначил: язык не позволяет мне понять, где нештатные ситуации могут возникнуть, а где нет. Если авторы придумают, как решить этот вопрос, например, через алгебраические типы данных, тем более что в Kotlin они есть, я ничего против не имею. Например, в Rust это очень хорошо сделано. В Haskell чуть менее в силу того, что язык чисто функциональный.

        А вообще скатываться на личные аргументы некрасиво.
          +3
          А есть серьёзные языки, которые создаются как неудобные, что ли?

          Rust и C++ считаются? :)
          Дело не в том, что создаются как "неудобные", а в том, что "удобство" это одна из первоочерёдных задач.


          А если пакетов больше, то тогда уже не «чуть».

          По сравнению с public частью — всё-таки "чуть".


          язык не позволяет мне понять, где нештатные ситуации могут возникнуть, а где нет.

          Да OOM может вылететь от каждого оператора new, SOE может вылететь от каждого вызова функции — теперь что, оборачивать всю программу в try-catch? Kotlin не отбирает возможность определения throws, как и отлова исключений.


          А вообще скатываться на личные аргументы некрасиво.

          Вы меня не поняли. Я не скатывался в стиле "ниасилил", я сам через это проходил. После нескольких лет Java Kotlin (особенно до версии 1.0 и в некоторых моментах до 1.1) выглядел… мягко говоря непривычно. Я плевался чуть ли не от каждой фичи, которая была сделана не так, как в Java просто потому, что всё не Java, кроме Java. Я не понимал, почему авторы сделали то-то и то-то, от того отказались, заменили на то-то. Мне понадобилось некоторое время для изучения stdlib и других библиотек чтобы осознать, как стоит писать на Kotlin и почему не надо бездумно копировать практики с Java просто потому, что это тоже JVM язык.

            0

            Я же уже объяснил, почему удобство не может быть принципом. Для меня лично, например, Rust — намного более удобный язык для тех задач, для которых он предназначен, чем его альтернативы. В том-то и проблема, лаконичность может быть принципом, явность может быть принципом. Удобство — нет.


            По сравнению с public частью — всё-таки "чуть".

            А если у меня всего один модуль в проекте в силу каких-то причин? Тогда public и internal вообще никак отличаться не будут.


            Да OOM может вылететь от каждого оператора new

            Это всё ситуации, которые программа не должна обрабатывать. Я же говорю не про них.


            я сам через это проходил

            Если бы мне было просто непривычно, я бы ругался на гораздо большее, чем эти две особенности. Мне-то как раз Kotlin понравился сразу тем, что можно всё очень лаконично записать, и с функциональными возможностями мне очень комфортно, в частности, благодаря опыту с Rust и Haskell. И пишу я на нём уже давно. Но вот эти две вещи мешали всегда.

            • НЛО прилетело и опубликовало эту надпись здесь
              +1
              Есть языки, которые создаются без особого учёта удобства разработчика. Оно приносится в жертву академичности, безопасности, скорости выполнения или компиляции, универсальности и т. д., да и просто гонке за количеством фич. Да, удобство очень субъективно, особенно в мелочах, сильно зависит от конкретных сценариев использования, но «инструмент должен быть удобным в основных целевых сценариях использования» — это полноценный принцип разработки чего угодно, в том числе языка. По мнению разработчиков языка, конечно, которое может не совпадать с мнением большинства пользователей. Зато, когда удобство один из основных принципов, никогда не услышишь критики «это слишком удобно, сделайте что-нибудь» :) Можно удивляться, что кто-то считает неудобным то, что ты считаешь удобным и наоборот, но требования «сделайте неудобным» вряд ли услышишь. :)
                0
                «инструмент должен быть удобным в основных целевых сценариях использования» — это полноценный принцип разработки чего угодно, в том числе языка

                Это утверждение верно, но оно примерно как теория, которая может описать что угодно. То есть формально теория верна, но предсказательная сила у неё в итоге нулевая. Так и тут: поскольку это верно в отношении всего, то и знание об этом ничего не даёт относительно какого-то конкретного языка.

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

                    Мы же говорим не об инструментах вообще, а о языках программирования. Говорили бы мы о таких инструментах, как молоток — я бы всецело согласился.

                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Минимизация бойлерплейта пойдет как критерий удобства?

                        Для кого-то да. А кому-то удобнее видеть всё прописанным явно.


                        В яве эксепшены надо обкладывать бойлерплэйтом

                        В Java тоже есть выбор: либо обрабатывать, либо объявлять, что твой метод тоже бросает определённые исключения. Другое дело, что бывают кривые API, которые проверяемыми исключениями делают ошибки программиста.


                        Котлин этого не запрещает делать, и вот ни разу не вынуждает

                        Верно. Проблема в другом. Я её уже и в статье, и в других комментариях подробно описал.

                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            Да это не важно, как описанное называется. Смысл в том, что иногда люди предпочитают делать это, чем, например, запоминать больше ключевых слов.
              0
              А можно изучать и использовать Kotlin как первый JVM-язык, не попадая постоянно в ситуации, когда нужно лезть в документацию Java?
                –1

                Я бы ни за один JVM-язык не стал браться без знания Java, а за Kotlin тем более, потому что стандартная библиотека там призвана дополнять стандартную Java, а не заменять её. Но пересесть на Kotlin со знанием Java, мне кажется, нет никакого труда. Правда, если предыдущий опыт был основан на Java 7 и раньше, к функциональным конструкциям придётся какое-то время привыкать.

                  0
                  То есть на практике это всё-таки какая-то надстройка над Java, а не полноценный язык, в котором JVM лишь деталь реализации конкретной. :(
                    +1
                    Это вполне полноценный язык. Просто с учетом его особенностей переписывать стандартную библиотеку Java на Kotlin- бессмысленно. Во-первых она уже есть. Во-вторых отлажена и оптимизирована. В третьих Kotlin c Java стыкуется бесшовно. Что можно получить при ее переписывании на Kotlin кроме потери времени?

                    На самом деле можно учить и Kotlin как первый язык. Но тут встает вопрос как сильно вы готовы абстрагироваться от выполнения самой программы. Скажем когда мы начинаем учить язык программирования нам начинают постепенно вводить понятия: переменные, циклы, классы. И даже если вы их используете как заклинание, без глубокого понимания, программа все-равно работает. Здесь ситуация будет примерно такая-же. Вы будете знать «как», но не будете знать «почему». Если вас это устраивает, то почему-бы и нет.

                    Но тут есть еще один подводный камень. Язык молодой и призван облегчить жизнь Java-программистов. Поэтому большая часть авторов будет предполагать, что вы пишите на Java и переходите на Kotlin. Если предполагается, что этот так, то большую часть понятий будут не объяснять и обсасывать, а декларировать. Вы знаете как работает try-catch в Java. В Kotlin это работает вот так. Но вы-то не знаете. Готовы через это продираться с учетом того, что в результате все-равно не будете понимать половину кода или таки пойдете коротким путем по изучению сначала Java? Решать вам.

                    PS: А еще хорошо перед изучением Kotlin почитать Haskell. Хотя это сейчас к большей части языков применимо ;)
                  +2
                  Как первый язык вообще вряд-ли, а вот как первый jvm язык вполне. Я например именно так его и изучал. У меня нет опыта разработки на Java и при этом я вполне спокойно пишу на Kotlin. Да иногда приходится смотреть в исходники библиотек написанных на Java, но для меня это не особо страшно, видимо опыт работы сперва с С++, а потом с php помог в этом деле.
                    –1
                    В принципе Java-код в различных книгах по паттернам и т. п. мне понятен, больше беспокоит именно стандартные и популярные библиотеки, фреймворки и т. п. Грубо, буду часами гуглить как вывести запись в лог в Kotlin, а окажется что надо было гуглить сразу Java. И непонятно как определять что гуглить.

                    Ещё рассказы о Java-инфраструктуре пугают. Вроде достаточно загрузить JDK для разработки и JRE для запуска, но что на глаза не попадётся, то всякие Ant, Maven, Gradle упоминаются вскользь но как «маст хэв», причём по ощущениям даже не в проекте, а где-то сбоку, глобально. В Kotlin с этим как?
                      0
                      Ну мне гуглить ррешения специально для Java не приходилось, но в ответах на SO java-решения всплываю частенько. Они будут работать, но зачастую будут не идеоматичны для Kotlin. И в большинстве случаев для котлина существуют либо свои решения, либо обертки над java-библиотеками приводящие их к стилю Kotlin. Но вообще по мере набора популярности платформ js и native все больше народу пишет библиотеки только для Kotlin без оглядки на java.

                      Про инфраструктуру. Так-как я пишу на котлине под адроид, то из инфраструктуры мне кроме gradle ничего больше не нужно. Gradle хватает за глаза. Ставим jdk и android studio она подтянет sdk, а gradle у нее в комплекте, и все можно писать. Манипуляций с инфраструктурой не сильно больше чем в любом другом языке.
                  –1
                  А о исключениях можно спорить вечно… Кто-то считает, что обязательно должны быть проверяемые исключения и компилятор должен насиловать мозг на каждый чих, кто-то считает, что исключения — это зло (как минимум, в том виде, какие они в Java). Правда как всегда где-то посередине.

                  Вот моё мнение однозначно, что непроверяемые исключения — это плохо. Я, к слову, лучше всего знаю C# и считаю непроверяемые исключения заодно с их *censored* иерархией наследования главным недостатком C#. В Java по мне главный недостаток в отсутствии возможности объявления структур, хотя в C# есть проблема с их изменением. Последний вопрос по-человечески сделали в Swift, исключения тоже проверяемые, даже есть возможность запрета пустых указателей, но в то же время ест свои, мягко говоря, «особенности». Разве что в 5-м Swift что-то исправят, хотя б абстрактные классы добавят. К слову в Swift и варианта protected нет, вот это по мне по-настоящему неудобно.
                    +2

                    Используя протоколы, можно со спокойной совестью забыть об использовании protected

                      0

                      И как они помогут сделать override, не делая метод открытым для всех? Хорошо, конечно, что в них можно объявлять и конструкторы в отличие от интерфейсов в C#, но по мне что уж они точно не заменяют — так это абстрактные классы. Точнее заменить можно, но иногда это настолько громоздко, что я предпочитаю писать такую фигню:


                          var price: Int? {
                              return nil;//pseudo-abstract
                          }

                      надеясь, что в 5-м Swift или позже абстрактные классы всё ж будут. Ибо иногда получается, что едва ли не на каждое абстрактное свойство пришлось б создавать протокол.

                        0

                        Тем, что класс обладает функциями не благодаря наследованию(где и используют protected), а тем, что наследует протоколы.

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

                          Жаль, сейчас уже не могу найти ту страницу о возможном появлении абстрактных классов в Swift, с тех пор об этом написано слишком много белиберды, а Google ещё не научился отличать дельную информацию от белиберды.
                            0
                            В extension для каждого protocol. Протокол не хранит состояние, но может спокойно хранить поведение функции.
                  +12
                  Ожидал от статьи немного больше, чем жалобы на отсутствие checked exceptions и package private.
                    –1
                    Меня ещё огорчила в паре случаев работа с вариантностью и границами переменных типа, но это слишком уж узкие случаи, чтобы писать про это в статье. А если ждали, что я буду разносить язык в пух и прах, то это я и не стану делать, в нём ведь действительно решения в основном удачные.
                    +1
                    А мне мешает вот это
                    github.com/JetBrains/kotlin-native/issues/733
                    А просвета на самом деле нет, как и в jruby, грузится несколько тысяч классов, похоже, поэтому так медленно. Упреждаю комментарии про IDE и инкрементальную сборку — речь идет о TDD. Запускается 1 тест c нуля. Прелоадеры — тоже, со времен ruby/jruby считаю, что проблем от них больше, чем пользы.
                      +13
                      • Пакетов нет в во второй платформе, в которую компилируется Kotlin — это JavaScript
                      • Там же нет и проверяемых исключений
                      • В самой JVM исключения тоже не делятся на проверяемые и непроверяемые, это фича только языка.
                      • Более того, есть "хаки", позволяющие выкинуть проверяемое исключение из метода, в котором оно не объявлено.
                      • И вообще, работая с JVM надо быть готовым к тому, что исключение может выкинуться из абсолютно любой точки выполнения. Это прописано в спецификации. Поэтому try-catch-finally надо писать не "для галочки", а там где реально надо сделить за потоком выполнения.
                      • Не знаю, сколько времени вы потратили на написание статьи, но разработчки Kotlin потратили на исследования и обсуждения не один год. Всем фичам языка там есть своя веская причина, правда, это далеко не всегда документировано.
                        0
                        Пакетов нет в во второй платформе

                        А чем их отсутствие в JS мешает проверять область видимости при компиляции Kotlin?


                        Там же нет и проверяемых исключений

                        Но если "это фича только языка", опять же, почему тогда они должны поддерживаться в JS?


                        Поэтому try-catch-finally надо писать не "для галочки", а там где реально надо сделить за потоком выполнения.

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


                        Не знаю, сколько времени вы потратили на написание статьи

                        При чём тут написание статьи? Я использую инструмент, которым трудно решить определённые проблемы, и я их описал. А "годы исследований и обсуждений" — это, по сути, апелляция к авторитету.


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

                        0
                        можно сделать так, чтобы метод был виден наследникам, как в своём пакете, так и вне пакета (protected)
                        Не совсем так. Protected это видимость для всех внутри пакета и для наследников в других пакетах.
                          0
                          Я в курсе, просто там формулировка двусмысленная. Перефразировал более корректно.
                          +1

                          Мне тоже не хватает package private.
                          Всё-таки мне кажется, что удобно разделять подсистемы по пакетам и скрывать детали их реализации, выставив для других частей приложения чёткий интерфейс и спрятав то, что напрямую использовать не следует.
                          Создавать 10-20 модулей в приложении только ради этого — не решение.
                          И аргумент создателей о том, что пакеты не защищены от добавления левых классов в другом модуле, не очень убедителен — это бросается в глаза и нельзя сделать случайно (и вряд ли так кто-то будет делать без веской причины). Чего не скажешь об использовании классов из соседнего пакета, которые для этого не предназначены.
                          Видимость внутри модуля — очень полезная вещь для библиотек. Но и видимость внутри пакета тоже нужна.

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

                            Да и не актуален начиная с Java 9. Один модуль не может подложить классы в те же пакеты другому модулю. Поэтому, кстати, JetBrains и заменили kotlin-stdlib-jre7 и kotlin-stdlib-jre8 на kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8: в первых двух один и тот же пакет был в обеих библиотеках.

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

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