Что является одной из основных функциональных особенностей языка kotlin по сравнению с языком java
Перейти к содержимому

Что является одной из основных функциональных особенностей языка kotlin по сравнению с языком java

  • автор:

Что является одной из основных функциональных особенностей языка Kotlin?

На вопрос от VK Testers про особенностях языка программирования Kotlin по сравнению с языком Java прежде чем ответить, нужно знать, что язык Kotlin вошел в число официальных для написания приложений для Android и в некоторых моментах превосходит язык Java, в частности, при программировании на Kotlin исключается ошибка при попытке доступа к null значению, потому что система типов Kotlin различает так называемые nullable ссылки.

Null Safety, «нулевая безопасность» — это и есть особенность языка Kotlin.

Язык программирования Kotlin

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

Особенности разработки на языке Kotlin

Особенности разработки на языке Kotlin

Совместимость с Java

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

Статическая типизация

Кotlin применяет статическую типизацию, что означает, что типы переменных определяются на этапе компиляции и не могут изменяться во время выполнения программы. Это существенно увеличивает надежность кода, так как многие ошибки и проблемы могут быть выявлены на ранних стадиях разработки, до того как программа будет запущена. Благодаря этой особенности, Kotlin помогает избегать многих типичных ошибок, таких как неправильное обращение к объектам (NullPointerException).

Функциональное программирование

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

Безопасность

Одним из ключевых преимуществ Kotlin является его система безопасности. Как уже упоминалось, Kotlin предоставляет инструменты для избегания ошибок на этапе компиляции. Например, в Kotlin отсутствует понятие «nullable» по умолчанию, что означает, что переменные не могут хранить значение «null» без явного указания. Это позволяет избегать множества ошибок, связанных с «null pointer», что часто является источником серьезных проблем в других языках.

Конструкции языка

Kotlin предоставляет разработчикам обширный набор конструкций для более удобной и эффективной работы с данными и логикой программы. Включая условные операторы (if, when), циклы (for, while), операторы присваивания и множество других инструментов. Комбинация этих конструкций делает код на Kotlin более читаемым и легко поддерживаемым.

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

Сфера применения языка Kotlin

Разработка Android-приложений

Одной из ключевых и важных областей применения Kotlin является разработка мобильных приложений для платформы Android. В 2017 году Kotlin был объявлен официальным языком программирования для Android, и с тех пор он стремительно набирает популярность среди Android-разработчиков. Основные характеристики и преимущества Kotlin:

  • Официальная поддержка Google: Google официально поддерживает Kotlin в Android Studio, предоставляя плагины и инструменты для более эффективной разработки приложений под Android.
  • Улучшенная безопасность: Kotlin помогает избежать многих распространенных ошибок, таких как NullPointerException, благодаря своей системе типов и безопасности на этапе компиляции.
  • Сокращение объема кода: Kotlin позволяет писать более краткий и выразительный код по сравнению с Java, что упрощает разработку и обслуживание Android-приложений.
  • Функциональное программирование: Возможности функционального программирования Kotlin делают его идеальным выбором для разработки современных Android-приложений.

Серверная разработка

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

Desktop-приложения

Хотя Kotlin не так широко используется для разработки desktop-приложений, он предоставляет возможности для создания кросс-платформенных приложений. Kotlin/Native, подпроект языка Kotlin, позволяет разрабатывать desktop-приложения, которые могут быть запущены на разных операционных системах, таких как Windows, macOS и Linux. Это может быть полезно для создания инструментов или приложений с небольшой клиентской базой.

Веб-разработка

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

Общие задачи программирования

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

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

Преимущества программирования на Kotlin

Преимущества программирования на Kotlin

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

  • Увеличение производительности: Kotlin позволяет писать код более компактно и выразительно, что ускоряет процесс разработки. Благодаря статической типизации и функциональным возможностям, Kotlin также способствует увеличению производительности приложений.
  • Улучшенная безопасность: Kotlin предоставляет множество инструментов для обеспечения безопасности кода. Это позволяет избегать многих ошибок на этапе компиляции, что сокращает время и ресурсы, затрачиваемые на отладку.
  • Чистый и читаемый код: Kotlin способствует написанию более чистого и читаемого кода благодаря своему синтаксису и функциональным конструкциям. Это делает поддержку и сопровождение проекта более простыми задачами.
  • Экосистема: Kotlin имеет богатую экосистему библиотек и инструментов, что упрощает разработку и расширение функциональности ваших приложений.

Недостатки языка Kotlin

Несмотря на множество преимуществ, Kotlin также имеет некоторые недостатки:

  • Страх перед изменениями: Переход с Java на Kotlin может вызвать определенный страх у некоторых разработчиков. Это связано с тем, что изменение используемого языка программирования всегда сопровождается переучиванием и адаптацией к новому синтаксису.
  • Ограниченное использование в некоторых областях: В некоторых областях разработки, таких как системное программирование или разработка игр, Kotlin может быть менее популярным выбором по сравнению с другими языками.

С чего начать изучение новичку

С чего начать изучение новичку

Если вы новичок в мире программирования и хотите начать изучение Kotlin, вот несколько шагов, с которых стоит начать:

  • Изучите основы языка: Начните с изучения основ синтаксиса Kotlin. Понимание базовых конструкций, переменных, операторов и функций является ключевым шагом в освоении языка.
  • Практикуйтесь: Практика — это основа для освоения любого языка программирования. Создавайте небольшие проекты, решайте задачи и участвуйте в открытых исходных проектах на Kotlin.
  • Изучайте библиотеки и фреймворки: Kotlin имеет богатую экосистему библиотек и фреймворков. Изучение их использования поможет вам разрабатывать более сложные приложения.
  • Общайтесь с сообществом: Сообщество Kotlin активно и дружелюбно. Участвуйте в форумах, читайте блоги и следите за обновлениями языка.

Тенденции рынка и оплата труда специалистов

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

Перспективы разработки на Kotlin

Перспективы разработки на Kotlin

Постоянное развитие: Одним из ключевых аспектов перспектив разработки на Kotlin является активное развитие самого языка. Компания JetBrains, создавшая Kotlin, продолжает инвестировать в его развитие и поддержку. Новые версии Kotlin регулярно выпускаются с улучшениями и новыми функциями, что делает язык более мощным и удобным для разработчиков.

Расширение экосистемы: Экосистема Kotlin постоянно растет. С каждым годом появляются новые библиотеки, фреймворки и инструменты, разработанные специально для Kotlin. Это упрощает разработку приложений и расширяет функциональность языка. Kotlin уже активно используется в таких областях, как мобильная разработка (Android), веб-разработка, бэкенд-разработка и даже разработка игр.

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

Развитие Android-платформы: Особенно важно отметить, что Kotlin стал официальным языком разработки для Android. Google активно поддерживает Kotlin в Android Studio, предоставляя инструменты и ресурсы для разработчиков. Это значит, что Kotlin будет продолжать доминировать в области мобильной разработки на Android, и его популярность будет расти параллельно с ростом популярности платформы Android.

Удобство для разработчиков: Кotlin обеспечивает удобство и удовольствие в работе для разработчиков. Его синтаксис чист и легко читаем, что упрощает сопровождение кода. Также Kotlin предоставляет множество возможностей для написания более эффективного и выразительного кода, что сильно ценится разработчиками.

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

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

Практическое применение Kotlin в стартапах и энтерпрайзе

Disclaimer: Я не имею никакого отношения к JetBrains, не получаю денег от продвижения Kotlin или от написания данной статьи. Весь материал — это лишь отражение моего личного опыта.

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

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

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

Kotlin против Java

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

На мой взгляд, Java зародилась как мощный ответ прежде всего C++. Поэтому в ней чувствуется сильное его влияние со всеми его недостатками. Проблема в том, что за 25 лет существования Java, она продемонстрировала значительную консервативность по сравнению с другими языками. В то время как множество языков экспериментировали с ключевыми словами типа async , с пунктуацией языка (точки с запятой, скобки и пр.), Oracle хранила исключительную верность «традиционным принципам», что привело лишь к моральному устареванию самого языка.

Печально то, что разрыв лишь нарастает, несмотря на попытки его сократить со стороны Oracle. Например, дата-классы в Kotlin появились в самых ранних версиях, а некий их аналог в Java появился лишь в 14 версии, полагаю, как раз под давлением конкуренции с JetBrains.

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

Если вам в вашем проекте требуется «возить лес в Сибири» вдали от селений и с полным отсутствием бензина, то, вероятно, паровоз вам действительно подойдет лучше. Но большинство проектов все же находятся не в столь экзотических условиях. Поэтому тот факт, что на Kotlin проект получается раза в 2-3 компактнее, более легкочитаемый, более надежный в плане защиты от NullPointerException , должен стать серьезным аргументом. Именно эти особенности и позволяют Kotlin снижать в 2 раза время разработки и количество багов, а в итоге, стоимость создания и владения программным продуктом.

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

  1. Множество элементов, сокращающих бойлерплейт: data class, getters&setters — в разы сокращает количество кода.
  2. Более продуманная и юзабельная структура классов: вместо HashMap, например, Map (иммутабельный) и MutableMap (мутабельный) — позволяет с легкостью входить в язык новичкам и концентрироваться на бизнесовой постановке, а не на способах реализации.
  3. Прямо из коробки идут различные защиты, например от null :

var str: String = "" str = null // Здесь будет ошибка компиляции, а не runtime var nullableStr: String? = null // а так можно

Проблемы перехода на Kotlin

При обсуждении проекта с командами, возникают одни и те же вопросы по внедрению Kotlin. Давайте пройдемся по ним.

Где взять Kotlin разработчиков?

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

В любом случае, любой разработчик, знакомый с Java, вполне успешно и быстро переходит на Kotlin (см. следующий вопрос).

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

Kotlin разрабатывался на базе Java и совместим с ним на 100%. Поэтому для перехода на него не обязательно проходить какие-то специализированные курсы. Начать разрабатывать на Kotlin можно с первого дня. Базовых отличий не так много между языками. Например, if работает и там, и там практически одинаково, дополнительно в Kotlin его можно использовать в виде выражения:

val x: Int = if (a == 1) 2 else 3

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

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

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

Как быть с легаси кодом?

Kotlin полностью интероперабелен с Java, что позволяет прямо в программе на Kotlin использовать код, написанный в Java. Также в саму Intellij Idea включен плагин, который на лету умеет конвертировать куски кода (либо весь файл) из Java в Kotlin. Надо понимать, что качество автоконвертации не сравнится с работой человека, но тем не менее, инструмент очень полезный.

Также Kotlin позволяет писать код одновременно на Java и Kotlin в одном модуле. Для этого можно настроить сборщик либо на расширения, либо на директории, разнеся их в src/main/java и src/main/kotlin . Тут опять нужно знать о нюансах: код из Java легко подхватывается в коде на Kotlin, но для обратной совместимости приходится прилагать определенные усилия. Например, по умолчанию в Kotlin вместо статических методов используются объекты-компаньоны и для экспорта в Java требуется применить аннотацию @JvmStatic .

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

У нас стартап, зачем нам Kotlin?

Довольно часто для стартапов применяется даже не Java, а такие языки как PHP, Python, JavaScript (NodeJS). В каких-то случаях это может быть оправдано, но надо иметь в виду следующее. Перечисленные языки могут себя неплохо показать для прототипирования, но из-за отсутствия строгой типизации, они меньше подходят для долгосрочных, крупных проектов. Просто потому, что строгая типизация позволяет избегать множества ошибок на этапе даже не компиляции, а на этапе написания кода в IDE. Ошибки, допущенные из-за отсутствия проверки типов, выливаются в простои в боевом режиме, приводя к убыткам. Поэтому вам нужно тщательно взвесить все выгоды от быстрой разработки на языках без строгой типизации и убытки от их применения в продуктовом режиме.

Довольно часто бытует мнение, что первую продажу можно сделать на каком-нибудь PHP, а затем мигрировать на Java/Kotlin. Мой опыт говорит, что сделать первую продажу можно вообще без кода хоть на PHP, хоть на Kotlin. Но времени для перехода с одного языка на другой на практике никто не даст при развитии стартапа. Каждый день будут задачи, которые нужно сделать вчера.

Безусловно, на любом языке можно написать плохо. Но PHP, Python, JS дают гораздо больше возможностей для низкокачественного кода, чем Java/Scala/Kotlin. И чем хуже изначально написанный код, тем хуже он поддерживается дальше, приводя к еще большим костылям. Я сталкивался со стартапом, который стартовал нормально, но за 7 лет своей работы получил совершенно нечитаемый и неподдерживаемый код на Python. Ситуация оказалась патовой: его нельзя было просто так закрыть из-за наличия обязательств, и нельзя было продолжить развивать из-за необходимости полностью переписать проект, на что бюджета уже не было.

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

Особенности разработки на Kotlin

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

Ненулевые переменные и свойства класса

Kotlin предоставляет мощнейший элемент защиты от null . Приводит это к тому, что работать с nullable переменными просто неудобно — их ведь нужно всегда проверять на null . Хоть такая проверка выполняется очень просто в Kotlin, хочется избегать ее чрезмерного использования. Достигается это тем, что при создании большинства классов я определяю пустой элемент:

data class SomeClass( var x: String = "", var y: Int = -1 ) < companion object < val NONE = SomeClass() >>

, который и используется при инициализации в качестве значения по умолчанию вместо null .

Существует множество случаев, когда нам просто необходимо делать свойства нулевыми. Например, в API или в хранилище мы не можем быть уверенными, что нам придет всегда non-nullable значение. Как раз здесь и играет свою роль DDD (domain driven design). Мы для отправки данных или для сохранения делаем отдельные DTO-классы, которые содержат в себе нулевые поля, а в рабочие модели мы их мапим со всеми проверками:

data class StorableObject( var id: String? = null, var data: String? = null ) < // Место размещения мапера зависит от задачи constructor(wm: WorkModel): this( < it != -1 >?.toString(), data = wm.data.takeIf < it.isNotBlank() >) // Место размещения мапера зависит от задачи fun toWorkModel(): WorkModel = WorkModel( ?: -1, data = data ?: "" ) >

Таким образом, простое использование Null-safety в Kotlin стимулирует нас применять практику DDD, что в свою очередь делает наш код более гибким, защищенным от изменений API или внутренней логики.

Kotlin Multiplatform (KMP)

KMP — это довольно мощное средство и его стоит использовать в проектах при интеграции с JS и Native компонентами системы. На практике, KMP вполне неплохо себя показывает при описании API для потребления Single Page Application (SPI) типа React, Angular, Vue, а также мобильными приложениями под Android и iOS.

Но лично я предпочитаю разрабатывать фронтенд на Dart/Flutter, а с этим фреймворком простой интеграции через KMP пока не существует. Интеграцию между бэкендом на Kotlin и фронтендом на Dart можно реализовать с использованием OpenAPI, AsyncAPI, gRPC и других кодогенераторов.

На бэкенде KMP может быть применен, например, при использовании Serverless архитектуры, в том числе Google Cloud Functions, Yandex Cloud Functions, AWS Lambda и др. аналогов. Особенность бессерверных архитектур в том, что в них важна скорость старта приложения. Поэтому код на Python или NodeJs может показывать гораздо более низкое время отклика, чем на JVM. Использование KMP позволяет компилировать код в JavaScript или в Native executable, что вполне решает проблему быстрого старта.

Недостатки Kotlin

Наверное есть какие-то недостатки более бытового характера. Но для меня самым существенным недостатком является отсутствие фронтенд-фреймворка для Kotlin. Ktor, при всех его преимуществах, не достиг уровня фронтенд-фреймворка. Я как-то видел набор в Jetbrains фронт-разработчиков и думаю, что вскорости такой фреймворк будет анонсирован. Однако, на сегодняшний день на фронтенде я предпочитаю использовать Google Flutter, который использует язык Dart. Возможно для кого-то окажется более приемлемым переход на Dart как на фронтенде, так и на бэкенде вместо Котлина.

Заключение

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

  1. Kotlin — это действительно мощный инструмент, который превосходит классическую Java по многим параметрам. Поэтому я ожидаю, что в ближайшие 5 -10 лет произойдет существенное вытеснение им последней с рынка.
  2. Перевод реальных проектов на Kotlin не создает непреодолимых препятствий, наоборот, его уместно осуществлять на легаси-проектах, так и новых; как в корпорациях, так и в стартапах.
  3. При том, что переход на Kotlin довольно прост, недостаточный опыт чреват Java-стилем, что неэффективно. Для более полноценного использования этого инструмента, рекомендую получение опыта, привлечение экспертов, либо прохождение практических курсов разработчиками.
  4. Использование Kotlin может заметно влиять на архитектуру проекта, делать ее более стройной, а также позволяет применять техники, которые неприменимы в Java, в частности Kotlin Multiplatform.
  5. Пока не существует достаточно мощного фронтенд-фреймворка на Kotlin. Кто-то может предпочесть Котлину Dart, который можно использовать одновременно на всех платформах как на фронтенде, так и на бэкенде.
  • kotlin
  • управление проектами
  • выбор языка программирования
  • стартапы
  • Программирование
  • Kotlin

Язык программирования Kotlin

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

07.11.2011 Андрей Бреслав

  • Ключевые слова / keywords:
  • Платформы
  • Программное обеспечение
  • Software

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

История разработки «альтернативных» языков на платформе Java насчитывает более десятилетия, однако распространение такие языки получили лишь относительно недавно. На волне популярности динамически типизированных языков поднялись JRuby, Groovy и Clojure, а среди статически типизированных языков следует упомянуть Scala, Fantom и Gosu. Сам язык Java тоже не стоит на месте [1], но его развитие осложнено как необходимостью сохранения обратной совместимости, так и непростой судьбой компании Sun Microsystems, поглощенной корпорацией Oracle.

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

Разработка проекта Kotlin началась летом 2010 года, в июле 2011-го проект был официально анонсирован и на сайте размещено описание языка. Выпуск публичной бета-версии компилятора запланирован на начало 2012 года.

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

Совместимость с Java. Платформа Java — это прежде всего экосистема: кроме «официальных» продуктов компании Oracle, в нее входит множество проектов с открытым кодом: библиотек и фреймворков разного профиля, на базе которых строится огромное количество приложений. Поэтому для языка, компилируемого для этой платформы, очень важна совместимость с существующим кодом, который написан на Java. При этом необходимо, чтобы существующие проекты могли переходить на новый язык постепенно, то есть не только код на Kotlin должен легко вызывать код на Java, но и наоборот.

Статические гарантии корректности. Во время компиляции кода на статически типизированном языке происходит множество проверок, призванных гарантировать, что те или иные ошибки не произойдут во время выполнения. Например, компилятор Java гарантирует, что объекты, на которых вызываются те или иные методы, «умеют» их выполнять, то есть что в соответствующих классах эти методы реализованы 1 . К сожалению, кроме этого очень важного свойства, Java почти ничего не гарантирует. Это означает, что успешно скомпилированные программы завершаются с ошибками времени выполнения (вызывают исключительные ситуации). Ярким примером является разыменование нулевой ссылки, при котором во время выполнения вызывается исключение типа NullPointerException. Важным требованием к новому языку является усиление статических гарантий. Это позволит обнаруживать больше ошибок на этапе компиляции и, таким образом, сокращать затраты на тестирование.

Скорость компиляции. Статические проверки упрощают программирование, но замедляют компиляцию, и здесь необходимо добиться определенного баланса. Опыт создания языков с мощной системой типов (наиболее ярким примером является Scala) показывает, что такой баланс найти непросто: компиляция зачастую становится неприемлемо долгой. Вообще, такая характеристика языка, как время компиляции проекта, может показаться второстепенной, однако в условиях промышленной разработки, когда объемы компилируемого кода очень велики, оказывается, что этот фактор весьма важен — ведь пока код компилируется, программист зачастую не может продолжать работу. В частности, быстрая компиляция является одним из важных преимуществ Java по сравнению с C++, и Kotlin должен это преимущество сохранить.

Лаконичность. Известно, что программисты зачастую тратят больше времени на чтение кода, чем на его написание, поэтому важно, чтобы конструкции, доступные в языке программирования, позволяли писать программы кратко и понятно. Java считается многословным языком (ceremony language — «церемонный язык»), и задача Kotlin — улучшить ситуацию в этом смысле. К сожалению, строгие методы оценивания языков с точки зрения их лаконичности развиты довольно слабо, но есть косвенные критерии; один из них — возможность создания библиотек, работа с которыми близка к использованию предметно-ориентированных языков (Domain-Specific Language, DSL). Для создания таких библиотек необходима определенная гибкость синтаксиса в совокупности с конструкциями высших порядков; наиболее распространены функции высших порядков, то есть функции, принимающие другие функции в качестве параметров.

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

Инструментальная поддержка. Современные программисты активно используют различные автоматизированные инструменты, центральное место среди которых занимают интегрированные среды разработки (Integrated Development Environment, IDE). Десятилетний опыт, накопленный в компании JetBrains, показывает, что определенные свойства языка могут существенно затруднять инструментальную поддержку. При разработке Kotlin мы учитываем этот факт и создаем IDE одновременно с компилятором.

Основные элементы языка

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

Объявления в Kotlin объединяются в пространства имен (namespace), и функция может быть объявлена непосредственно внутри пространства имен:

namespace example < fun max(a : Int, b : Int) : Int < if(a < b) returna returnb > >

Объявление функции предваряется ключевым словом fun, а типы параметров указываются после двоеточия, следующего за именем параметра. Аналогично обозначается и тип возвращаемого значения функции. Такой синтаксис следует традициям языков «функционального мира», например ML и Scala. Он позволяет легко опускать типовые аннотации, если тип может быть выведен компилятором из контекста автоматически.

Переменные в Kotlin, как и в Scala, объявляются с помощью ключевых слов val (неизменяемые переменные) и var (изменяемые):

var sinSum : Double = 0.0 for (x in xs) < val y = sin(x) sinSum += y >

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

Классы. Основным инструментом декомпозиции в Kotlin, как и в других объектно-ориентированных языках, являются классы. При объявлении класса список параметров конструктора указывается непосредственно в заголовке:

class IntPair(x: Int, y: Int)

Экземпляры класса создаются прямым вызовом конструктора; ключевого слова new в Kotlin нет:

val xy = IntPair(x, y)

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

class MyList(length: Int): List, Serializable

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

Внешние функции. Еще один механизм «расширения» типов в Kotlin — это внешние функции (extension function — «функция-расширение»). Такие функции могут быть объявлены вне какого-либо класса и при этом вызываться так, как будто они были объявлены внутри. Вот пример объявления внешней функции для типа Int:

fun Int.abs() : Int < if (this < 0) return -this else return this >

Тип, расширяемый данной функцией, указывается перед ее именем и отделяется точкой. Это соответствует объявлению «неявного» параметра, который внутри функции обозначается ключевым словом this. Например, в нашем примере функция abs() расширяет тип Int, и неявный параметр this является целым числом. Такую функцию можно вызывать с помощью операции «точка», как и функции — члены класса:

val x = (-1).abs()

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

Внешние функции связываются статически, то есть не являются виртуальными (virtual).

Управляющие конструкции. When

Kotlin поддерживает традиционные для императивных языков управляющие конструкции if, for и while, на которых мы не будем останавливаться подробно, а также конструкцию when — операцию ветвления, которую можно рассматривать как расширенную версию традиционного оператора switch:

when (x) < 1 =>print(“One”) 2, 3 => print(“Two or three”) else => print(“Unknown number”) >

Слева от знака «=>» указывается выражение или список выражений, разделенных запятыми, называемый условием. Если аргумент конструкции when (в нашем примере — переменная x) равен хотя бы одному из этих выражений, выполняется тело данного условия, то есть выражение или блок, указанный справа от знака «=>». Условия проверяются последовательно, сверху вниз. Если ни одно из условий не выполнено, выполняется код, указанный после слова else. В отличие от switch, при использовании when условия не являются метками, поэтому не требуется заканчивать тело условия словом break.

Кроме простого сравнения на равенство, when позволяет проверять аргумент на принадлежность коллекции, с помощью операции in:

when (x) < in set => print(“in set”) in 1..10 => print(“1..10”) else => print(“Default branch”) >

После ключевого слова in указывается выражение, имеющее любой тип, поддерживающий метод contains(). В частности, может быть указана коллекция (как в первом условии в данном примере) или промежуток (как во втором). Промежутки могут быть образованы как целыми, так и дробными числами, причем крайние значения (в данном примере 1 и 10) включаются.

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

when (x) < is String => println(“String”) is Int => println(“Int”) is Array => println(“Array of Double”) >

Система типов

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

fun isEmpty(s: String?): Boolean

Знак вопроса после имени типа (String) означает, что ссылка s указывает на объект класса String или имеет значение null. Результат функции isEmpty, в свою очередь, должен быть булевым значением и не может иметь значения null, поскольку соответствующий тип не проаннотирован знаком вопроса.

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

return s.length() == 0 // Ошибка: разыменование нулевой ссылки

Необходимо явно проверить, ссылается ли s на существующий объект:

if (s != null) < return s.length() == 0 // s точно ссылается на существующий объект > else < return true >
return (s == null) || s.length() == 0//Оператор||обеспечивает проверку

Часто встречаются длинные цепочки вызовов, каждый из которых может вернуть null. В результате мы получаем несколько вложенных условий, проверяющих, что вернул каждый из вызовов в цепочке. Чтобы избежать загромождения кода, в Kotlin поддерживается оператор безопасного вызова, обозначающийся «?.»:

a?.getB()?.getC()?.getD()

Если a не равно null, выражение a?.getB() возвращает a.getB(), а в противном случае — null.

Автоматическое приведение типа. Мы приводили пример того, как компилятор учитывает информацию, содержащуюся в условиях, и разрешает разыменовывать уже проверенные ссылки. Аналогичный механизм автоматически вставляет операцию приведения типа, если ранее в программе проверялось соответствующее условие. Оператор проверки типа (аналог instanceof в Java) в Kotlin называется is:

val x : Object = . if (x is String)

В этом примере ссылка x проверяется на принадлежность к типу String, и, если проверка прошла успешно, на экран выводится длина строки. При вызове функции length() компилятор автоматически вставляет приведение x к типу String, поскольку оно безопасно в этом месте программы.

Автоматическое приведение типа работает для всех условных конструкций: if, when, while, ||, && и т. д.

Функции высших порядков

Пользователи функциональных языков программирования хорошо знакомы с функциями высших порядков: механизмом, позволяющим передавать функции в качестве аргументов другим функциям, записывать функции в переменные и т. д. Как уже говорилось, этот механизм значительно облегчает создание библиотек. В объектно-ориентированных языках функции высших порядков обычно эмулируются с помощью паттерна Strategy [2]. Например, для того чтобы реализовать фильтрацию для коллекций произвольного типа, необходимо параметризовать функцию filter() объектом, который «умеет» отвечать на вопрос, нужно ли включить данный элемент в результирующую коллекцию. Этот объект является стратегией фильтрации. В функциональном языке создавать стратегию не нужно — можно просто передать функцию.

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

val predicate: fun(x: Int): Boolean =.

В данном примере переменная predicate имеет функциональный тип «fun (x: Int): Boolean», то есть хранимое ею значение является функцией, принимающей целочисленный параметр и возвращающей булевское значение. В частности, мы можем вызвать эту функцию:

val yesOrNo = predicate(1)

Такая функция может использоваться, например, при фильтрации набора целых чисел:

fun Collection.intFilter(predicate: fun(x: Int): Boolean): Collection

А для произвольных коллекций можно ее обобщить с помощью типовых параметров (generic):

fun Collection.filter(predicate : fun(x : T) : Boolean) : Collection < val result = ArrayList() for (x in this) if (predicate(x)) result.add(x) return result >

(Здесь приведена лишь наивная реализация функции filter(). Более реалистичная реализация требует ленивых вычислений, но эта тема выходит за рамки данной статьи.)

Самое интересное — как задавать значение аргумента, имеющего функциональный тип. Для этого широко используются уже упоминавшиеся функциональные литералы:

ints.filter( x % 2 == 0>)

В данном примере аргумент внешней функции filter() представляет собой функциональный литерал, то есть короткое объявление функции. Он обязательно заключается в фигурные скобки, до символа «=>» следуют объявления параметров, а после — тело функции, причем оператор return не требуется, поскольку результатом считается последнее выражение в теле литерала. Таким образом, из коллекции ints будут выбраны только четные числа, поскольку наш литерал возвращает true, только если остаток от деления x на 2 равен нулю.

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

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

ints.filter() // Вызов аналогичен предыдущему примеру

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

ints.filter // Вызов аналогичен двум предыдущим примерам

Такой синтаксис позволяет записывать преобразования коллекций в стиле, напоминающем LINQ [3]:

ints.select . where // Среди квадратов элементов коллекции // выбрать четные

(Здесь функция where() делает то же, что и функция filter().)

Кроме того, данная конвенция делает вызовы функций больше похожими на привычные управляющие конструкции. Приведем еще один пример. Функция synchronized() принимает два параметра: объект синхронизации (монитор) и функцию. Во время выполнения сначала захватывается монитор, далее в блоке try..finally выполняется функция, а затем монитор освобождается:

fun synchronized(l : Lock, body : fun() : T) : T < l.lock() try < return body() > finally < l.unlock() >>

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

synchronized (myLock) < // Код, который необходимо выполнить >

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

Предметно-ориентированные языки

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

val homeDir = ". " module("org.jetbrains.test") < // Объявление модуля depends(MavenRepo("junit.junit@4.8.2")) // Зависимость importNamespace("java.lang") // Импорт по умолчанию importNamespace("org.junit") // Импорт по умолчанию sourceRoot("$homeDir/src") // Путь к исходным файлам testSourceRoot("$homeDir/tests") // Путь к тестам >

Несмотря на то что перед нами обыкновенная программа на Kotlin, она выглядит как программа на специализированном языке, предназначенном для декларативного описания модулей. Такой подход к описанию структур данных весьма популярен в Groovy и других динамических языках, поскольку позволяет избежать громоздких и трудно читаемых дескрипторов, написанных на XML. В Groovy такой подход известен под названием Builders [4]. Ярким примером его использования является библиотека Gradle [5].

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

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

// Абстрактный модуль abstract class Module(name : String) < val dependencies : List = ArrayList() >
// Модуль, состоящий из классов, написанных на Kotlin class KotlinModule(name : String) : Module(name) < fun dependency(module : Module) < dependencies.add(module) >>

// Модуль на основе репозитория Maven class MavenRepo(name : String) : Module(name)

Определим функцию module(), создающую новый модуль:

fun module(name : String, init : fun KotlinModule.() : Unit) : KotlinModule < val result = KotlinModule(name) result.init() return result >

Данная функция является функцией высшего порядка, поскольку параметр init сам является функцией, причем внешней функцией: об этом говорит тип KotlinModule, указанный перед (пустым) списком параметров в функциональном типе. Это означает, что функцию module() можно вызывать следующим образом:

module(“org.jetbrains.test”) < // тело функции init >

Это похоже на пример сценария, который мы уже видели. Заметим, что внутри функционального литерала доступен неявный параметр this типа KotlinModule (поскольку этот литерал имеет тип «внешняя функция»), и мы можем его использовать:

module(“org.jetbrains.test”) < this.dependency(MavenRepo("junit.junit@4.8.2")) this.dependency(anotherModule) // . >

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

module(“org.jetbrains.test”) < dependency(MavenRepo("junit.junit@4.8.2")) dependency(anotherModule) // . >

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

html < head < title > body < h1 p < +"This is some text. For more see the" a(href = "http://jetbrains.com/kotlin") > >

Мы рассмотрели несколько наиболее интересных особенностей языка Kotlin, но за рамками остались такие возможности языка, как обобщенные типы (generic), встраиваемые функции (inline function), представление типов во время выполнения (reified type), поддержка делегирования, переопределение операторов и др. Об этих возможностях, а также о ходе работы над проектом можно прочесть на странице проекта.

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

Литература

  1. Bloch J. Effective Java. Second Edition. — Prentice Hall, 2008.
  2. Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. — СПб.: Питер, 2007.
  3. Троэлсен Э. Язык программирования С# 2008 и платформа. NET 3.5. — М.: Вильямс, 2010.
  4. Groovy Builders.
  5. Gradle: a better way to build.

1 За исключением случаев несогласованной раздельной компиляции. — Прим. автора.

2 Операция is также позволяет проводить сопоставление с образцом (pattern matching) — Прим. автора.

Андрей Бреслав (andrey.breslav@jetbrains.com) — ведущий разработчик языка Kotlin, компания JetBrains.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *