Kotlin. Программирование для профессионалов
Қосымшада ыңғайлырақҚосымшаны жүктеуге арналған QRRuStore · Samsung Galaxy Store
Huawei AppGallery · Xiaomi GetApps

автордың кітабын онлайн тегін оқу  Kotlin. Программирование для профессионалов

 

Джош Скин, Дэвид Гринхол, Эндрю Бэйли
Kotlin. Программирование для профессионалов. 2-е изд.
2022

Переводчик Е. Матвеев


 

Джош Скин, Дэвид Гринхол, Эндрю Бэйли

Kotlin. Программирование для профессионалов. 2-е изд.. — СПб.: Питер, 2022.

 

ISBN 978-5-4461-2319-3

© ООО Издательство "Питер", 2022

 

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

 

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

— Э. Б.

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

— Д. Г.

Посвящается Бейкеру, моему маленькому непоседе.

— Дж. С.

Благодарности

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

Брайан Силлз (Bryan Sills), Майкл Йотив (Michael Yotive), Нейт Соттек (Nate Sottek), Джереми Шерман (Jeremy Sherman) и Марк Дьюран (Mark Duran) великодушно делились своим мнением относительно второго издания книги.

• Эрик Максвелл (Eric Maxwell) проводил обучение по ранней версии второго издания и подготовил материал для глав о сопрограммах, каналах и потоках данных.

• Лорен Клингман (Loren Klingman) и Джейк Соуэр (Jake Sower) дали нам информацию для главы, посвященной Kotlin/JS.

• Дрю Фицпатрик (Drew Fitzpatrick) полностью прочитал раннюю версию второго издания и снабдил нас материалами о Kotlin Multiplatform и Kotlin/Native.

• Лив Витал (Liv Vitale), Кристиан Кер (Christian Keur), Закари Вальдовски (Zachary Waldowski) и Дэвид Хауз (David House) постоянно делились опытом работы на других платформах и участвовали в обсуждениях Kotlin/JS и Kotlin/Native. Спасибо вам за то, что вы отвечали на наши странные вопросы, когда мы пытались казаться более осведомленными, чем были на самом деле.

• Джавонтай Макэлрой (Javontay McElroy), наш талантливый дизайнер из Big Nerd Ranch, создал шпаргалку IntelliJ IDEA для печатного издания. Ты смело вступил на неизведанную территорию печатных материалов и разработал дизайн с нуля — благодарим!

• Эрик Уилсон (Eric Wilson), Мэдисон Уицлер (Madison Witzler), Фрэнклин О’Нил (Franklin O’Neal) и CJ Best — спасибо великим умам отдела повышения квалификации Big Nerd  Ranch. Наши занятия — и как следствие, эта книга — стали возможными лишь благодаря вашей усердной работе.

• Когда мы рассказывали нашему редактору Элизабет Холадей (Elizabeth Holaday) о своих планах на второе издание книги, первое, что мы от нее услышали: «Похоже, обновление будет серьезным». Конечно, ее оценка была абсолютно верной. Спасибо за самоотверженную работу по совершенствованию книги, доработку ее сильных сторон и устранение недостатков.

• Саймону Пэйменту (Simone Payment), нашему корректору и редактору, спасибо за помощь в последних приготовлениях к выпуску книги.

• Элли Волкхаузен (Ellie Volckhausen) создала дизайн обложки. Это круто!

• Крис Лопер (Chris Loper) из IntelligentEnglish.com разработал и подготовил печатную и цифровую версию книги. Мы также постоянно пользовались его инструментарием DocBook.

Аарону Хиллеглассу (Aaron Hillegass) и Стейси Генри (Stacy Henry) — наша благодарность! Эта книга не появилась бы на свет без Big Nerd Ranch — компании, которую Аарон основал, а Стейси возглавляет.

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

Представляем Kotlin

В 2011 году компания JetBrains анонсировала альтернативу языкам Java и Scala —язык программирования Kotlin, код которого тоже выполняется под управлением виртуальной машины Java (Java Virtual Machine). Шесть лет спустя Google объявил об официальной поддержке Kotlin как языка разработки для операционной системы Android.

И Kotlin быстро превратился из просто «перспективного» в язык поддержки приложений для лидирующей мобильной операционной системы. Сегодня крупные компании вроде Google, Uber, Netflix, Capital One, Amazon и других официально приняли на вооружение Kotlin, чему способствовали его компактность, современные возможности и полная совместимость с Java.

Почему Kotlin?

Чтобы оценить привлекательность Kotlin, стоит сначала разобраться, какое место в современном мире разработки ПО занимает Java. Код на Kotlin выполняется под управлением Java Virtual Machine, поэтому эти два языка тесно взаимосвязаны.

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

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

Kotlin — это не просто улучшенный язык для виртуальной машины Java. Это мультиплатформенный язык общего назначения: на Kotlin можно писать нативные приложения для macOS, Windows и iOS, приложения на Java­Script и, конечно, приложения для Android. В последнее время компания JetBrains прилагает значительные усилия для разработки кросс-платформенных возможностей; Kotlin Multiplatform предоставляет уникальную возможность совместного использования кода разными приложениями, что привело к росту популярности Kotlin за пределами виртуальной машины Java.

Для кого написана эта книга

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

Поддержка Android может стать мотивом для изучения Kotlin, но наша книга не ограничивается рассказом о программировании для Android. Более того, весь код в книге не зависит от фреймворка Android. Тем не менее, если вас интересует именно использование Kotlin для разработки Android-приложений, здесь вы найдете основные приемы, которые упростят процесс написания приложений для Android на Kotlin.

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

Как пользоваться этой книгой

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

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

Хотим добавить, что спешить не стоит: делайте паузы и обращайтесь к документации Kotlin по ссылке kotlinlang.org/docs/reference, если какая-то тема вас особенно заинтересовала, — и экспериментируйте.

Для любознательных

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

Задания

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

Мы рекомендуем создавать копии проектов, прежде чем браться за новые задания; проекты в книге часто базируются на основе предыдущих решений, и ваши действия не должны нанести им вред. Решения к упражнениям из книги можно загрузить по адресу bignerdranch.com/kotlin-2e-solutions/.

Шрифтовые обозначения

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

Переменные, их значения и типы набраны моноширинным шрифтом.

Для листингов с кодом мы также используем моноширинный шрифт. Код в листинге, который вам предлагается дописать, выделен полужирным. Если же код из листинга нужно удалить, то он будет зачеркнут. В следующем примере вам предлагается удалить строку кода, объявляющую переменную y, и добавить в код переменную z:

var x = "Python"

var y = "Java"

var z = "Kotlin"

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

Правила оформления кода JetBrains — kotlinlang.org/docs/coding-conventions.html.

• Руководство по стилю Google — developer.android.com/kotlin/style-guide.

Заглядывая вперед

Не спешите, работая над примерами из этой книги. Когда вы освоите синтаксис Kotlin, вы убедитесь, что этот язык — ясный, гибкий и прагматичный. А пока этого не случилось, просто вдумчиво изучайте его — всегда полезно.

От издательства

Язык Kotlin был создан российской компанией JetBrains. Само название Kotlin происходит от острова Котлин в Финском заливе, недалеко от Санкт-Петербурга.

При переводе терминов использовался англо-русский глоссарий Kotlin, предоставленный JetBrains.

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

Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

Часть I. Первые шаги

Первые две главы книги посвящены основам работы IntelliJ IDEA, ведущей IDE для разработки приложений Kotlin. Вы создадите простой проект, в котором освоите базовые возможности языка. А начнем мы с рассказа о типах Kotlin — механизме классификации данных, с которыми вы будете работать.

1. Ваше первое приложение на Kotlin

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

Установка IntelliJ IDEA

IntelliJ IDEA — это интегрированная среда разработки (integrated development environment, IDE) для языка Kotlin, созданная, как и сам язык, командой JetBrains. Чтобы приступить к работе, загрузите IntelliJ IDEA Community Edition с сайта JetBrains по ссылке jetbrains.com/idea/download1 (рис. 1.1).

 

Рис. 1.1. Загрузка IntelliJ IDEA Community Edition

Затем выполните указания для своей системы, приведенные в инструкции по установке и настройке на сайте JetBrains: Jetbrains.com/help/idea/installation-guide.html#standalone.

IntelliJ IDEA, или просто IntelliJ, помогает писать хорошо структурированный код на Kotlin. Кроме того, она упрощает процесс разработки с помощью встроенных инструментов для запуска, отладки, исследования и рефакторинга кода. Узнать, почему мы рекомендуем IntelliJ для создания кода на Kotlin, вы можете в разделе «Для любознательных: зачем использовать IntelliJ?».

Ваш первый проект на Kotlin

Поздравляем: теперь у вас есть язык программирования Kotlin и мощная среда разработки. Осталось только научиться свободно на нем «разговаривать». Повестка дня — создать Kotlin-проект.

Большинство проектов, над которыми вы будете работать в этой книге, связаны с игрой в стиле фэнтези, в которой герой выполняет героические миссии, побеждает злобных монстров, спасает города от опасностей и вообще делает то, что положено делать героям. В первом проекте мы построим «доску поручений» — систему, которая будет направлять нашего героя по имени Мадригал (Madrigal) к задачам, требующим его внимания. Запустите IntelliJ. Откроется окно приветствия Welcome to IntelliJ IDEA (рис. 1.2).

 

Рис. 1.2. Диалоговое окно с приветствием

(Если вы уже запускали IntelliJ после установки, среда может автоматически загрузить последний открывавшийся проект. Чтобы вернуться к окну приветствия, закройте проект командой меню File

Close Project.)

Выберите вариант New Project. IntelliJ отобразит новое окно New Project, как показано на рис. 1.3.

 

Рис. 1.3. Диалоговое окно New Project

В окне New Project в левой панели выберите Kotlin (рис. 1.4).

В IntelliJ можно писать код и на других языках, кроме Kotlin, например на Java и Groovy, для которых имеется встроенная поддержка. Установив дополнительные плагины, вы получите возможность писать код в IntelliJ на таких языках, как Python, Scala, Dart и Rust (список далеко не полный). Выбрав вариант Kotlin в левой части окна New Project, вы указываете, что собираетесь работать на Kotlin.

Теперь рассмотрим настройки проекта на центральной панели диалогового окна.

В верхней части окна New Project введите в поле Name текст bounty-board. Поле Location заполняется автоматически. Вы можете оставить информацию в этом поле без изменений или выбрать другую папку, щелкнув на значке с папкой справа от поля.

 

Рис. 1.4. Создание проекта Kotlin

Меню Project Template содержит три категории параметров: JVM, Multiplatform и Kotlin/JS (также возможна категория Experimental). Выберите вариант Application под заголовком JVM. Тем самым вы сообщаете IntelliJ, что собираетесь писать код на Kotlin, предназначенный для виртуальной машины Java.

Код на Kotlin может компилироваться для любой из трех платформ, поддерживаемых языком: виртуальной машины Java, платформ x86 и ARM (из раздела Multiplatform списка Project Template), а также JavaScript (сокращается до JS). Иногда для обозначения «специализации» Kotlin в зависимости от платформы используются термины Kotlin/JVM, Kotlin/Native и Kotlin/JS.

Kotlin/JVM — то, что большинство разработчиков имеют в виду, когда говорят о Kotlin. Kotlin/JVM обозначает код для виртуальной машины Java. Аналогично Kotlin/JS используется для вывода кода JavaScript из кода Kotlin. А Kotlin/Native обозначает все программы Kotlin, компилируемые в нативный машинный код. Как подсказывает название категории Multiplatform, Kotlin/Native можно использовать для построения программных продуктов для многих платформ: библиотек iOS, нативных настольных приложений, встроенных устройств (для самых амбициозных) и т.д.

Далее мы будем везде применять сокращенное название виртуальной машины Java — JVM (Java Virtual Machine). Эта аббревиатура часто используется в сообществе Java-разработчиков. Узнать больше о программировании для JVM можно в разделе «Для любознательных: программирование для JVM» в конце главы.

Kotlin/JVM — наиболее зрелая из трех платформ. Она изначально поддерживалась Kotlin и продолжает оставаться одной из самых распространенных платформ для разработки на этом языке. В большей части книги будет использоваться Kotlin/JVM. Мы приняли такое решение по нескольким причинам.

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

• Java — чрезвычайно зрелый язык, и для него существует множество API, которые мы используем в книге. Высокоуровневые API упрощают выполнение некоторых задач (по сравнению с низкоуровневыми платформенными API).

• Примеры, приведенные в книге, выполняются внутри IDE, но их также можно запускать из любого терминала. Это несколько снижает ценность JavaScript как платформы, так как он выполняется в основном в браузере, а не в тер­минале.

В особенностях Kotlin для этих трех платформ много общего, и то, что вы знаете о Kotlin/JVM, применимо к Kotlin/JS и Kotlin/Native. Не все API доступны на всех платформах, поэтому мы будем особо отмечать API, которые используются только с JVM. Использование Kotlin с другими платформами мы более подробно рассмотрим в главах 24, 25 и 26.

Но вернемся к настройке нового проекта. В группе Build System выберите вариант Gradle Groovy. В списке Project JDK выберите версию Java, которая будет использована при компоновке с JDK (Java Development Kit). Мы рекомендуем применять версии от Java 8 (часто обозначается Java 1.8) до Java 15.

Если нужная версия Java отсутствует в раскрывающемся списке, значит, IntelliJ не находит установленную копию на вашем компьютере. Если вы точно знаете, что пакет JDK у вас установлен, и хотите использовать именно эту версию, выберите команду Add JDK... и найдите копию на диске. В противном случае IntelliJ установит для вас JDK при помощи команды Download JDK.... Мы рекомендуем выбрать в поле Version значение 15, а в поле Vendor — значение AdoptOpenJDK (HotSpot), но подойдет любой вендор. Когда IntelliJ завершит установку, можно браться за дело.

Зачем нужен JDK для написания программы на Kotlin? JDK открывает среде IntelliJ доступ к JVM и инструментам Java, которые необходимы для перевода кода на Kotlin в байт-код (об этом ниже). В принципе, подойдет любая версия, начиная с шестой. Но по нашему опыту, на момент написания этой книги JDK 8 и более ранние версии работали наиболее стабильно.

Если диалоговое окно выглядит так, как показано на рис. 1.5, щелкните на кнопке Next, а затем на кнопке Finish в следующем диалоговом окне для подтверждения введенных значений.

 

Рис. 1.5. Настройка проекта

IntelliJ сгенерирует проект с названием bounty-board и отобразит проект в стандартном двухпанельном представлении (рис. 1.6). IntelliJ создаст на диске папку и ряд подпапок с файлами проекта в месте, путь к которому указан в поле Project location.

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

В окне инструментов проекта выводится список файлов, используемых в проекте bounty-board, как показано на рис. 1.7.

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

 

Рис. 1.6. Стандартное двухпанельное представление

Папки .gradle и .idea на рис. 1.7 могут быть скрыты при открытии проекта в проводнике файлов. Это служебные папки, к которым вам обращаться не стоит; в ­папке .gradle хранятся кэши, используемые системой сборки, а в папке .idea — файлы с параметрами конфигурации проекта и IDE.

 

Рис. 1.7. Представление проекта

Папка gradle и такие файлы, как gradlew и gradlew.bat, относятся к Gradle Wrapper — автономной копии системы построения Gradle, которая требуется  для установки Gradle на вашем компьютере. Файлы build.gradle, gradle.properties и settings.gradle содержат параметры конфигурации системы сборки Gradle. Они определяют различные данные: имя проекта, уровень языка, модули проекта и зависимости вашего проекта. Не трогайте эти автоматически сгенерированные файлы.

Категория External Libraries содержит информацию о библиотеках, от которых зависит проект. Раскрыв эту категорию, вы увидите, что среда IntelliJ автоматически добавила Java и некоторые стандартные библио­теки Kotlin как зависимости вашего проекта. (­Узнать больше о структуре проектов в IntelliJ можно на сайте JetBrains — jetbrains.org/intellij/sdk/docs/basics/project_sctructure.html. Структура проектов Gradle также рассматривается на сайте Gradle — docs.gradle.org/current/userguide/organizing_gradle_projects.html.)

В папке src/main/kotlin будут сохраняться все файлы проекта. А теперь пришло время создавать и редактировать ваш первый файл на Kotlin.

Ваш первый файл на Kotlin

Щелкните правой кнопкой мыши на папке src/main/kotlin в окне с инструментами проекта. В открывшемся контекстном меню выберите сначала пункт New, а затем Kotlin File/Class (рис. 1.8).

 

Рис. 1.8. Создание нового файла Kotlin

В диалоговом окне New Kotlin Class/File в поле Name введите Main, затем дважды щелк­ните на пункте File в открывшемся списке (рис. 1.9).

 

Рис. 1.9. Присваивание имени файлу

IntelliJ создаст в проекте новый файл src/main/kotlin/Main.kt и отобразит его содержимое в окне редактора справа (рис. 1.10). Расширение .kt указывает, что файл содержит исходный код на языке Kotlin, подобно тому как расширение .java сообщает, что файл содержит код Java или .py — код Python.

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

 

Рис. 1.10. Пустой файл Main.kt в окне редактора

Листинг 1.1.  "Hello, world!" на Kotlin (Main.kt)

fun main() {

    println("Hello, world!")

}

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

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

Конкретно эта функция — main — имеет особое значение в Kotlin. Она определяет начальную точку программы. Ее также называют точкой входа приложения; чтобы проект bounty-board (или любую другую программу) можно было запустить, в нем обязательно должна быть определена точка входа. Все проекты в этой книге начинаются с функции main.

Ваша функция main содержит одну инструкцию (иногда инструкции называют командами): println("Hello,world!"). println() — это тоже функция, встроенная в стандартную библиотеку Kotlin. Она доступна для всех платформ, поддержи­ваемых Kotlin. После запуска программа выполнит println("Hello,world!"), и IntelliJ выведет на экран строку, указанную в скобках (без кавычек — Hello, world!).

Запуск вашего файла на языке Kotlin

Когда вы закончите вводить код из листинга 1.1, IntelliJ отобразит зеленую стрелку

слева от первой строки кода (рис. 1.11). (Если значок не появился или вы видите красное подчеркивание под именем файла на вкладке или где-нибудь во введенном коде, то это значит, что в коде допущена ошибка. Исправьте введенный код: он должен точно совпадать с кодом в листинге 1.1.)

 

Рис. 1.11. Кнопка запуска программы

Пришло время оживить программу — пусть она поприветствует мир. Нажмите кнопку запуска. В появившемся меню выберите Run ‘MainKt’ (рис. 1.12). Тем самым вы сообщаете IntelliJ, что хотите посмотреть на программу в действии.

 

Рис. 1.12. Запуск Main.kt

После запуска IntelliJ выполнит код, содержащийся в фигурных скобках ({}), строку за строкой, и завершит выполнение. Также в нижней части окна IntelliJ появится новое инструментальное окно (рис. 1.13).

 

Рис. 1.13. Инструментальное окно Run (консоль)

Это — инструментальное окно запуска (run tool window), также известное как консоль (далее мы будем называть его именно так). Оно отображает информацию о происходящем после того, как IntelliJ запустит программу, и результаты, которые выводит программа. В консоли должно появиться сообщение Hello, world! и сообщение Process finished with exit code 0, что означает успешное завершение программы. Эта строчка завершает консольный вывод при отсутствии ошибок, и с этого момента мы больше не будем ее упоминать.

Компиляция и выполнение кода Kotlin/JVM

С момента выполнения команды Run 'Mainkt' до вывода Hello, World! в консоли происходит множество событий.

Прежде всего, IntelliJ компилирует код Kotlin, используя компилятор kotlinc-jvm. Это означает, что IntelliJ транслирует код на Kotlin в байт-код — язык, на котором «разговаривает» JVM. Если у kotlinc возникнут проблемы с переводом, он выведет сообщение об ошибке (ошибках), которое подскажет, что именно необходимо исправить. Однако если компиляция прошла гладко, IntelliJ перейдет к фазе выполнения.

В фазе выполнения байт-код, сгенерированный kotlinc-jvm, исполняется JVM. На консоли отображается все, что выводит программа в процессе выполнения, например текст, указанный в вызове функции println().

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

Для изучения материала этой книги вам не обязательно досконально понимать процесс компиляции в Kotlin. Тем не менее мы более подробно рассмотрим байт-код в главе 2.

Kotlin REPL

Иногда возникает необходимость протестировать маленький кусочек кода на Kotlin, чтобы посмотреть, что происходит при его выполнении, — по аналогии с тем, как вы записываете последовательность вычислений на листке бумаги. Такая возможность особенно полезна в процессе изучения языка. Вам повезло: IntelliJ предоставляет инструмент для быстрого тестирования кода без создания файла. Этот инструмент называется Kotlin REPL. Название объясним позже, а сейчас посмотрим, что он делает.

В IntelliJ откройте Kotlin REPL, выбрав в меню команду Tools

Kotlin
Kotlin REPL (рис. 1.14).

 

Рис. 1.14.  Открытие инструментального окна Kotlin REPL

IntelliJ отображает панель REPL в нижней части окна (рис. 1.15).

 

Рис. 1.15. Инструментальное окно Kotlin REPL

Код в REPL можно вводить так же, как в редакторе. Разница в том, что вы быстро получите результат без компиляции всего проекта.

(При запуске REPL могут появиться предупреждения красного цвета "running the REPL with outdated classes" — «запуск REPL с устаревшими классами». Обычно на такие предупреждения можно не обращать внимания. Общие проблемы платформы или JVM не нанесут вреда при использовании REPL, а поскольку в REPL вы не будете обращаться к коду из bounty-board, предупреждения об устаревших классах можно игнорировать.)

Введите следующий код в REPL.

Листинг 1.2. "Hello, Kotlin!" (REPL)

println("Hello, Kotlin!")

После ввода текста нажмите Command-Return (Ctrl-Enter), чтобы выполнить код в REPL. Через мгновение вы увидите результат под введенной строкой — это должен быть текст Hello, Kotlin! (рис. 1.16).

 

Рис. 1.16. Выполнение кода

REPL — это сокращение от Read, Evaluate, Print, Loop (прочитать, выполнить, вывести, повторить). Вы вводите фрагмент кода после подсказки и отправляете его в обработку, нажав на зеленую кнопку запуска слева в окне REPL или Command-Return (Ctrl-Enter). Далее REPL читает код, выполняет его и выводит результат. Завершив выполнение, REPL возвращает управление пользователю и дает возможность повторить процесс.

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

Для любознательных: зачем использовать IntelliJ

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

писать синтаксически и семантически правильный код с помощью таких функций, как подсветка синтаксиса, контекстные подсказки, автодополнение;

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

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

Кроме того, так как и язык Kotlin, и среда разрабатывались командой JetBrains, интеграция Kotlin с IntelliJ была тщательно проработана, что делает работу легкой и приятной. Также стоит упомянуть, что среда IntelliJ была заложена в основу Android Studio, поэтому горячие клавиши и средства, описанные в этой книге, вы сможете использовать и там (если потребуется, конечно).

Для любознательных: программирование для JVM

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

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

 

Рис. 1.17. Процесс компиляции и выполнения кода

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

Так как Kotlin может транслироваться в байт-код для JVM, он считается JVM-языком. Вероятно, Java стал самым известным JVM-языком, потому что он был первым. Впоследствии появились другие JVM-языки — Scala, Groovy и Kotlin, которые были призваны устранить некоторые недостатки Java с точки зрения разработчиков.

Задание: арифметические вычисления в REPL

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

С помощью REPL изучите, как работают арифметические операторы в Kotlin: +, , *, /, %. Например, введите (9+12)*2 в REPL. Соответствует ли вывод вашим ожиданиям?

Если хотите узнать больше, просмотрите список математических функций, поддерживаемых стандартной библиотекой Kotlin, по адресу kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/ и поэкспериментируйте с ними в REPL. Например, min(94, -99) выведет наименьшее из двух чисел, указанных в скобках.

На этой стадии изучения языка вам стоит ознакомиться с веб-сайтом kotlinlang.org и особенно с размещенной на нем документацией. Если вы планируете использовать Kotlin вне JVM, обратите внимание на цветные метки рядом со ссылками на API. Они обозначают, какие платформы поддерживает данный API. Многие API, например absoluteValue (рис. 1.18), работают на всех платформах, поддерживаемых Kotlin.

 

Рис. 1.18. Поддержка платформ в документации API Kotlin

1 Есть русскоязычная версия страницы загрузки https://www.jetbrains.com/ru-ru/idea/download. Но упоминаемая далее инструкция доступна только на английском языке. — Примеч. ред.

Есть русскоязычная версия страницы загрузки https://www.jetbrains.com/ru-ru/idea/download. Но упоминаемая далее инструкция доступна только на английском языке. — Примеч. ред.

IntelliJ IDEA — это интегрированная среда разработки (integrated development environment, IDE) для языка Kotlin, созданная, как и сам язык, командой JetBrains. Чтобы приступить к работе, загрузите IntelliJ IDEA Community Edition с сайта JetBrains по ссылке jetbrains.com/idea/download1 (рис. 1.1).

2. Переменные, константы и типы

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

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

Типы

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

Чтобы увидеть, как работает проверка типа, отредактируйте файл Main.kt из проекта bounty-board, созданный в главе 1. Если вы закрыли среду IntelliJ, запустите ее заново. Скорее всего, проект bounty-board откроется автоматически, так как IntelliJ при запуске открывает последний проект, с которым вы работали. Если этого не произошло, выберите bounty-board в списке недавних проектов в середине окна приветствия или при помощи команды File

Open Recent
bounty-board.

Объявление переменной

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

В файле Main.kt создайте первую переменную с именем playerLevel и присвойте ей значение.

Листинг 2.1. Объявление переменной playerLevel (Main.kt)

fun main() {

    println("Hello, world!")

    var playerLevel: Int = 4

    println(playerLevel)

}

Здесь экземпляр типа Int присваивается переменной с именем playerLevel. Давайте подробнее рассмотрим, что у нас получилось.

Для определения переменной используется ключевое слово var, которое объявляет новую переменную. После ключевого слова мы задаем имя переменной.

Затем указывается тип переменной Int. Это означает, что в playerLevel будет храниться целое число.

И наконец, мы используем оператор присваивания (=), чтобы присвоить значение в правой части (экземпляр типа Int, а именно 4) переменной в левой части (playerLevel).

На рис. 2.1 показано объявление playerLevel в виде диаграммы.

 

Рис. 2.1. Порядок объявления переменной

После объявления значение переменной можно вывести в консоль с помощью функции println.

Запустите программу, щелк­нув на кнопке запуска рядом с функцией main и выбрав Run ‘Mainkt’. Также для этого можно воспользоваться кнопкой запуска на панели инструментов IntelliJ. В консоли отобразится число 4 — то значение, которое было присвоено playerLevel.

Теперь попробуйте присвоить playerLevel значение "thirty-two". (Зачеркнутая строка означает, что код надо удалить.)

Листинг 2.2. Присваивание "thirty-two" переменной playerLevel (Main.kt)

fun main() {

    println("Hello, world!")

    var playerLevel: Int = 4

    var playerLevel: Int = "thirty-two"

    println(playerLevel)

}

Снова запустите main при помощи кнопки запуска. На этот раз компилятор Kotlin сообщит об ошибке:

    e: Main.kt: (3, 28): Type mismatch: inferred type is String but Int was

                                        expected

Набирая код, вы могли заметить, что "thirty-two" подчеркнуто красным. Так IntelliJ показывает вам обнаруженную ошибку. Наведите указатель мыши на "thirty-two", отобразится описание проблемы (рис. 2.2).

 

Рис. 2.2. Подсказка: обнаружено несоответствие типа

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

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

Чтобы устранить ошибку, надо присвоить переменной playerLevel другое значение типа Int — например, заменить "thirty-two" целым числом 4.

Листинг 2.3. Исправление ошибки типа (Main.kt)

fun main() {

    println("Hello, world!")

    var playerLevel: Int = "thirty-two"

    var playerLevel: Int = 4

    println(playerLevel)

}

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

Листинг 2.4. Переменной playerLevel присваивается 5 (Main.kt)

fun main() {

    println("Hello, world!")

    var playerLevel: Int = 4

    println(playerLevel)

    

    println("The hero embarks on her2 journey to locate the enchanted sword.")

    playerLevel = 5

    println(playerLevel)

}

Снова выполните обновленную функцию main, чтобы увидеть, как работает присваивание. В консоли выводится число 5 в отдельной строке после сообщения The hero embarks on her journey to locate the enchanted sword (Герой отправляется в путешествие, чтобы найти заколдованный меч).

Оператор присваивания (=) связывает переменную с новым значением. Такое решение работает, но правильнее было бы увеличить playerLevel на 1, вместо того чтобы присваивать переменной значение 5. Увеличение уровня работает лучше, потому что ваш код станет более гибким — например, если вам понадобится изменить начальный уровень игрока.

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

Листинг 2.5. Увеличение playerLevel (Main.kt)

fun main() {

    println("Hello, world!")

    println("The hero announces her presence to the world.")

    var playerLevel: Int = 4

    println(playerLevel)

    

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel = 5

    playerLevel += 1

    println(playerLevel)

}

После присваивания переменной playerLevel значения 4 используем оператор сложения с присваиванием (+=) для увеличения исходного значения на 1. Запустите программу снова. Вы увидите новое приветствие, а уровень игрока изменится с 4-го на 5-й, как и прежде.

Kotlin также предоставляет другие возможности присваивания значений. Так как playerLevel увеличивается на 1, вместо оператора += можно использовать оператор инкремента (++):

    playerLevel++

Для уменьшения значения на 1 можно использовать оператор декремента (--), а для уменьшения на произвольную величину — оператор вычитания с присваиванием (-=). Также существуют аналогичные операторы для умножения с присваиванием (*=) и деления с присваиванием (/=). Математические операторы мы рассмотрим более подробно в главе 5.

Встроенные типы языка Kotlin

Вы уже видели переменные типа Int, а также пользовались типом String при вызове функции println. Kotlin также поддерживает типы для работы со значениями «истина/ложь», списками и парами «ключ — значение». В табл. 2.1 перечислены наиболее часто используемые типы, доступные в Kotlin.

Таблица 2.1. Наиболее часто применяемые встроенные типы

Тип

Описание

Примеры

String (строка)

Текстовая информация

"Madrigal"

"happy meal"

Char (символ)

Один символ

'X'

Символ Юникод U+0041

Boolean (логический)

Истина/ложь

Да/Нет

true

false

Int (целочисленный)

Целое число

5

"Madrigal".length

Double (с плавающей запятой)

Дробные числа

3.14

2.718

List (список)

Коллекция элементов

3, 1, 2, 4, 3

"root beer", "сlub soda", "coke"

Set (множество)

Коллекция уникальных значений

"Larry", "Moe", "Curly", "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"

Map (ассоциативный массив)

Коллекция пар «ключ — значение»

"small" to 5.99, "medium" to 7.99, "large" to 10.99

Если какие-то типы вам неизвестны, не переживайте — вы познакомитесь с ними в процессе чтения книги. В частности, строки рассматриваются в главе 6, числа — в главе 5, а списки, множества, ассоциативные массивы (относящиеся к категории коллекций) — в главах 9 и 10.

Переменные, доступные только для чтения

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

Язык Kotlin позволяет объявлять переменные, доступные только для ­чтения, — значения таких переменных нельзя изменить после присваивания.

Переменная, которую можно изменить, объявляется с помощью ключевого слова var. Чтобы объявить переменную, доступную только для чтения, используется ключевое слово val.

Переменные, которые могут изменяться, мы будем называть vars, а переменные, доступные только для чтения, — vals.

Добавьте определение val для хранения имени игрока и выведите его после вывода начального уровня игрока.

Листинг 2.6. Добавление val heroName (Main.kt)

fun main() {

    println("The hero announces her presence to the world.")

 

    val heroName: String = "Madrigal"

    println(heroName)

    var playerLevel: Int = 4

    println(playerLevel)

    

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel += 1

    println(playerLevel)

}

Запустите программу при помощи кнопки запуска рядом с функцией main или в строке меню. Значения playerLevel и heroName должны появиться в консоли:

    The hero announces her presence to the world.

    Madrigal

    4

    The hero embarks on her journey to locate the enchanted sword.

    5

Далее попытаемся присвоить heroName другое строковое значение оператором = и снова запустим программу.

Листинг 2.7.  Попытка изменения значения heroName (Main.kt)

fun main() {

    println("The hero announces her presence to the world.")

 

    val heroName: String = "Madrigal"

    println(heroName)

    var playerLevel: Int = 4

    println(playerLevel)

 

    heroName = "Estragon"

    

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel += 1

    println(playerLevel)

}

При попытке запуска в консоли появится следующее сообщение об ошибке компиляции:

    e: Main.kt: (9, 5): Val cannot be reassigned

Компилятор сообщил о попытке изменить val. После начального присваивания значение val нельзя изменить.

Удалите вторую операцию присваивания, чтобы исправить ошибку повторного присваивания.

Листинг 2.8.  Исправление ошибки повторного присваивания значения val  (Main.kt)

fun main() {

    println("The hero announces her presence to the world.")

    val heroName: String = "Madrigal"

    println(heroName)

    var playerLevel: Int = 4

    println(playerLevel)

 

    heroName = "Estragon"

    

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel += 1

    println(playerLevel)

}

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

Среда IntelliJ способна определить на основании статического анализа кода, когда var можно превратить в val. Если var не изменяется по ходу программы, IntelliJ предложит преобразовать его в val. Мы советуем следовать рекомендациям IntelliJ, если, конечно, вы не планируете писать код для изменения значений var. Чтобы увидеть, как выглядит рекомендация от IntelliJ, преобразуйте heroName в var.

Листинг 2.9. Замена heroName на var (Main.kt)

fun main() {

    println("The hero announces her presence to the world.")

 

    val heroName: String = "Madrigal"

    var heroName: String = "Madrigal"

    println(heroName)

    var playerLevel: Int = 4

    println(playerLevel)

    

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel += 1

    println(playerLevel)

}

Так как значение heroName нигде не изменяется, нет необходимости (и не следует) объявлять его как var. Обратите внимание, что среда IntelliJ выделила строку с ключевым словом var горчичным цветом. Если навести указатель мыши на ключевое слово var, IntelliJ сообщит о предлагаемом изменении (рис. 2.3).

 

Рис. 2.3. Подсказка: переменная нигде не изменяется

Как и ожидалось, IntelliJ предлагает преобразовать heroName в val. Чтобы подтвердить изменение, щелкните на ключевом слове var рядом с heroName и нажмите Option-Return (Alt-Enter). В появившемся меню выберите Change to val (рис. 2.4).

 

Рис. 2.4. Переменная становится неизменяемой

IntelliJ автоматически заменит var на val:

    val heroName: String = "Madrigal"

    println(heroName)

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

Автоматическое определение типов

Обратите внимание, что типы, которые вы указали для переменных heroName и playerLevel, выделены серым цветом в IntelliJ. Серый цвет показывает элементы, которые являются необязательными или не используются. Наведите указатель мыши на определение типа String, и IntelliJ объяснит, почему эти элементы необязательны (рис. 2.5).

 

Рис. 2.5. Избыточная информация о типе

Как видите, Kotlin считает, что ваше объявление типа избыточно. Что это значит?

Kotlin поддерживает механизм автоматического определения типов (type inference), что позволяет опустить типы для переменных, которым присваиваются значения при объявлении. Так как при объявлении переменной heroName при­сваивается значение типа String и переменной playerLevel присваивается значение типа Int, компилятор Kotlin автоматически определяет тип каждой переменной.

Подобно тому как среда IntelliJ помогает поменять var на val, она может помочь убрать ненужное объявление типа. Щелкните на объявлении типа String (: String) рядом с heroName и нажмите Option-Return (Alt-Enter). Затем щелк­ните на строке Remove exclicit type specification в появившемся меню (рис. 2.6).

 

Рис. 2.6. Удаление явного определения типа

Объявление типа : String исчезнет. Повторите процесс для playerLevel var, чтобы убрать : Int.

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

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

Type Info (или нажмите Control-Shift-P [Ctrl-Shift-P]). Результат показан на рис. 2.7.

 

Рис. 2.7. Вывод информации о типе

Константы времени компиляции

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

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

Константы времени компиляции могут иметь значения только одного из следующих базовых типов (использование более сложных типов может поставить под угрозу гарантию времени компиляции). Вы узнаете больше о конструировании типов в главе 14. Итак, вот допустимые базовые типы для констант времени компиляции:

String

Float

Byte

Int

Long

Char

Double

Short

Boolean

Имя героя игры (Madrigal) никогда не изменится — это главный персонаж, и у игрока нет возможности его сменить. Чтобы отразить эту неизменность в коде, можно задать значение имени константой. В файле Main.kt переместите переменную heroName над объявлением функции main и добавьте модификатор const.

Листинг 2.10. Объявление константы времени компиляции (Main.kt)

const val heroName = "Madrigal"

 

fun main() {

    println("The hero announces her presence to the world.")

 

    val heroName = "Madrigal"

    println(heroName)

    var playerLevel: Int = 4

    println(playerLevel)

 

    println("The hero embarks on her journey to locate the enchanted sword.")

    playerLevel += 1

    println(playerLevel)

}

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

В Kotlin принято использовать верблюжийРегистр (camelCase) для имен переменных и ЗМЕИНЫЙ_РЕГИСТР (SNAKE_CASE) для имен констант. Таким образом, вместо того чтобы присваивать новой константе имя heroName, правильнее было бы назвать ее HERO_NAME.

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

Щелкните правой кнопкой мыши на константе heroName, затем выберите ­команду Refactor

Rename... (рис. 2.8).

 

Рис. 2.8. Команда меню Refactor

Rename…

Имя константы выделяется цветом. Введите HERO_NAME, заменяя выделенный текст. В процессе ввода IntelliJ продолжает выделять константу, как показано на рис. 2.9.

 

Рис. 2.9. Переименование константы

В процессе ввода обратите внимание, что при вводе нового имени строка println(heroName) автоматически обновляется. IntelliJ находит все случаи ­употребления константы в вашем проекте и обновляет их, задавая новое имя. В проекте bounty-board замена затронет только одну строку кода, но в большом проекте IntelliJ может автоматически обновить множество файлов.

Когда вы завершите ввод нового имени, нажмите Return, чтобы подтвердить изменения.

Изучаем байт-код Kotlin

Из главы 1 вы узнали, что на Kotlin можно писать программы для виртуальной машины JVM, которая исполняет байт-код Java. Зачастую бывает полезно взглянуть на байт-код Java, который генерируется компилятором языка Kotlin и запускается под управлением JVM. Кое-где в этой книге мы будем рассматривать байт-код, чтобы понять, как конкретные особенности языка работают в JVM.

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

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

В файле Main.kt дважды нажмите клавишу Shift, чтобы открыть диалоговое окно Search Everywhere (поиск везде). Начните вводить: «show Kotlin bytecode» (показать байт-код Kotlin) и выберите из списка доступных действий Show Kotlin bytecode, как только оно появится (рис. 2.10).

Откроется инструментальное окно байт-кода Kotlin (рис. 2.11). (Также можно открыть его с помощью меню Tools

Kotlin
Show Kotlin Bytecode.)

 

Рис. 2.10. Вывод байт-кода Kotlin

 

Рис. 2.11. Инструментальное окно байт-кода Kotlin

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

Откроется новая вкладка Main.decompiled.java с Java-версией байт-кода, сгенерированного компилятором Kotlin для JVM (рис. 2.12).

 

Рис. 2.12. Декомпилированный байт-код

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

Найдите объявление переменной playerLevel:

    int playerLevel = 4;

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

О декомпилированном байт-коде Java мы подробнее расскажем в следующих главах. А пока закройте Main.decompiled.java (нажав Х на вкладке) и инструментальное окно байт-кода (используя значок _ в правом верхнем углу).

К сожалению, этот инструмент работает только с кодом Kotlin, предназначенным для JVM. Код Kotlin/JS транслируется в JavaScript, а код Kotlin/Native компилируется в низкоуровневый машинный код. Для этих платформ не генерируется байт-код, который можно было бы проанализировать. Декомпиляция чрезвычайно полезна при изучении Kotlin, хотя со временем, когда вы начнете более уверенно пользоваться языком, вы будете применять этот инструмент все реже.

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

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

Для любознательных: простые типы Java в Kotlin

В Java есть два вида типов: ссылочные и простые. Ссылочные определяются в исходном коде. Некоторые ссылочные типы также называются объектными, или упакованными (boxed), типами. Простые типы (часто их называют примитивами) не имеют определения в исходном коде и представлены ключевыми словами.

Имена ссылочных типов в Java всегда начинаются с прописной буквы; это признак того, что где-то в исходном коде находится определение этого типа. Вот как выглядит объявление playerLevel со ссылочным типом на языке Java:

     Integer playerLevel = 5;

Имена примитивных типов в Java начинаются со строчной буквы:

     int playerLevel = 5;

Для всех примитивов в Java имеется соответствующий ссылочный тип (но не все ссылочные типы имеют соответствующий простой тип). Зачем нужны эти две категории?

Часто ссылочные типы выбираются просто потому, что некоторые возможности языка Java доступны только при их применении. Например, механизм обобщения типов, с которым вы познакомитесь в главе 18, не работает с примитивами. Ссылочные типы также упрощают использование объектно-ориентированных возможностей Java. (Об объектно-ориентированном программировании и его особенностях в Kotlin разговор пойдет в главе 13.)

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

В отличие от Java, Kotlin поддерживает только один вид типов — ссылочный.

    var playerLevel: Int = 4

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

Если вы знакомы с Java, то, наверное, думаете сейчас: «Но примитивы работают производительнее ссылочных типов!» И это правда. Но давайте посмотрим, как выглядит переменная playerLevel в байт-коде, который вы видели раньше:

    int playerLevel = 4;

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

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

Задание: hasSteed

В главе 1 мы предлагали вам поэкспериментировать с математическими операциями в Kotlin REPL. Многие задания в других главах базируются на проекте, над которым вы работаете, — в данном случае bounty-board. Прежде чем приступать к заданиям, создайте копию своего проекта. Проекты многих глав вы будете создавать на материале предыдущих глав, и конечно же, ошибки неизбежны. Поэтому не ленитесь и делайте копии своих проектов, прежде чем вносить в них изменения.

Вот ваше первое задание для bounty-board. В нашей текстовой приключенческой игре игрок может приручить дракона или минотавра, чтобы ездить на нем. Объявите переменную с именем hasSteed (стать хозяином боевого коня), чтобы отслеживать, удалось ли игроку обзавестись средством передвижения. Задайте переменной начальное состояние, указывающее, что в данный момент средства передвижения нет.

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

Задание: «Рог единорога»

Представьте следующую сцену из игры.

Герой игры прибыл в таверну «Рог единорога». Трактирщик спрашивает:

«Вам нужна конюшня?»

«Нет, — отвечает тот, — у меня нет боевого коня. Но у меня есть пятьдесят монет, и я хочу выпить».

«Замечательно! — говорит трактирщик. — Могу предложить мед, вино и пиво. Что вы желаете?»

А теперь задание: добавьте после переменной hasSteed дополнительные переменные, необходимые для реализации сцены в таверне «Рог единорога», используя автоматическое определение типов и присваивая значения переменным при необходимости. Добавьте переменные для названия таверны, имени трактирщика и количества монет у игрока.

Обратите внимание: в таверне есть меню напитков, в котором можно сделать выбор. Каким типом можно воспользоваться для представления меню? За подсказкой обращайтесь к табл. 2.1.

Задание: волшебное зеркало

Отдохнув, герой готов отправляться на поиски приключений. А вы?

Герой обнаружил волшебное зеркало, которое показывает его HERO_NAME наоборот. Используя магию типа String, превратите строку HERO_NAME "Madrigal" в "lagirdaM", зеркальное отражение значения этой переменной.

Чтобы решить эту задачу, посмотрите описание типа String по адресу kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html. Там вы узнаете, что действия, которые поддерживает конкретный тип, обычно имеют очевидные названия (это подсказка).

2 В примерах кода авторы используют местоимения женского рода she, her для героя игры (возможно, из соображений политкорректности). На русском языке «герой» и «игрок» звучит более привычно, чем «героиня» или «игрокиня», поэтому в русской версии герой Мадригал будет мужского рода. — Примеч. ред.

В примерах кода авторы используют местоимения женского рода she, her для героя игры (возможно, из соображений политкорректности). На русском языке «герой» и «игрок» звучит более привычно, чем «героиня» или «игрокиня», поэтому в русской версии герой Мадригал будет мужского рода. — Примеч. ред.

    println("The hero embarks on her2 journey to locate the enchanted sword.")

Часть II. Базовый синтаксис

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

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