Привет, Хаброжители! Книга Джоша Скина и Дэвида Гринхола основана на популярном курсе Kotlin Essentials от Big Nerd Ranch. Яркие и полезные примеры, четкие объяснения ключевых концепций и основополагающих API не только знакомят с языком Kotlin, но и учат эффективно использовать его возможности, а также позволяют освоить среду разработки IntelliJ IDEA от JetBrains.
Неважно, опытный вы разработчик, который хочет выйти за рамки Java, или изучаете первый язык программирования. Джош и Дэвид проведут вас от основных принципов к расширенному использованию Kotlin, чтобы вы могли создавать надежные и эффективные приложения.
Мы (авторы) написали эту книгу для разработчиков разного калибра: опытных Android-разработчиков, которым не хватает возможностей Java, разработчиков серверного кода, заинтересованных в возможностях Kotlin, а также для новичков, решившихся на изучение эффективного компилируемого языка.
Эта книга может заинтересовать вас поддержкой Android, но она не ограничивается программированием на Kotlin для Android. Более того, в этой книге лишь одна глава — глава 21 — рассматривает приемы программирования на Kotlin для Android. Тем не менее если вас интересует тема использования Kotlin для разработки Android-приложений, эта книга познакомит вас с основными приемами, которые упростят процесс написания Android-приложений на Kotlin.
Несмотря на то что Kotlin подвергся влиянию некоторых других языков, вам ни к чему знать, как они устроены, чтобы успешно работать с Kotlin. Время от времени мы будем сравнивать код на Java и Kotlin. Если у вас есть опыт разработки в Java, это поможет вам понять связь между этими двумя языками. А если у вас нет такого опыта, примеры решения тех же задач на другом языке помогут вам понять идеи, повлиявшие на формирование Kotlin.
Эта книга не является справочником. Наша цель — последовательное обучение языку Kotlin. Вы будете работать с проектами и изучать язык в процессе работы. Для большего эффекта мы рекомендуем опробовать все примеры кода по ходу чтения книги. Работа с примерами поможет вам развить «мышечную» память и даст понимание, позволяющее переходить от одной главы к другой.
Каждая следующая глава основывается на предыдущей. Мы рекомендуем не пропускать главы. Даже если вы изучили тему, работая с другими языками, мы предлагаем хотя бы прочитать об этом здесь: многое в Kotlin реализовано иначе. Мы начнем с вводных тем, таких как переменные и списки, а затем перейдем к приемам объектно-ориентированного и функционального программирования, чтобы дать вам понять, что делает Kotlin таким мощным инструментом. К концу книги вы пройдете путь от новичка до продвинутого разработчика на Kotlin.
Хотим добавить, что спешить не стоит: развивайтесь, используйте документацию Kotlin по ссылке: kotlinlang.org/docs/reference, где есть ответы на многие возникающие в ходе экспериментов вопросы.
Расширения позволяют добавить функциональности типу без явного изменения объявления типа. Применяйте расширения с пользовательскими типами, а также с типами, над которыми у вас нет контроля, например List, String и другими типами из стандартной библиотеки Kotlin.
Расширения служат альтернативой наследованию. Они хорошо подходят для добавления функциональности в тип, если определение класса вам недоступно или класс не имеет модификатора open, позволяющего создавать подклассы.
Стандартная библиотека языка Kotlin часто использует расширения. Например, стандартные функции, которые вы изучили в главе 9, объявлены как расширения, и в этой главе вы увидите несколько примеров их объявления.
В этой главе мы сначала поработаем в проекте Sandbox, а затем применим полученные знания для оптимизации кода NyetHack. Начнем с того, что откроем проект Sandbox и создадим новый файл с именем Extensions.kt.
Ваше первое расширение позволяет добавить любую степень восторга в String. Объявите его в Extensions.kt.
Листинг 18.1. Добавление расширения для типа String (Extensions.kt)
Функции-расширения объявляются тем же способом, что и другие функции, но с одним отличием: определяя функцию расширения, вы также указываете тип, известный как принимающий тип, которому расширение добавляет возможностей. (Вспомните главу 9, где расширяемые типы мы называли «приемниками».) Для функции addEnthusiasm указан принимающий тип String.
Тело функции addEnthusiasm — это всего лишь одно выражение, которое возвращает строку: содержимое this и 1 или более восклицательных знаков, в зависимости от значения аргумента amount (1 — это значение по умолчанию). Ключевое слово this ссылается на экземпляр объекта-приемника, для которого вызвано расширение (в этом случае экземпляр String).
Теперь можно вызвать функцию addEnthusiasm для любого экземпляра String. Испытайте новую функцию-расширение, объявив строку в функции main и вызвав для нее функцию-расширение addEnthusiasm с выводом результата.
Листинг 18.2. Вызов нового расширения для экземпляра объекта-приемника String (Extensions.kt)
Запустите Extensions.kt и посмотрите, добавляет ли функция-расширение восклицательный знак в строку, как и задумывалось.
Можно ли создать подкласс String, чтобы добавить эту возможность экземплярам String? В IntelliJ посмотрите на исходный код объявления String, нажав клавишу Shift дважды для открытия диалогового окна Search Everywhere, и введите в поле для поиска «String.kt». Вы увидите такое объявление класса:
Так как ключевое слово open отсутствует в объявлении класса String, вы не сможете создать подкласс String, чтобы добавить новые возможности через наследование. Как ранее упоминалось, расширения — хороший вариант, если вы хотите добавить функциональности в класс, которым не можете управлять или который нельзя использовать для создания подкласса.
Расширения не полагаются на наследование, но их можно сочетать с наследованием для увеличения области видимости. Попробуйте сделать это в Extensions.kt: объявите расширение для типа Any с именем easyPrint. Так как расширение объявлено для Any, оно будет доступно для всех типов. В main замените вызов функции println вызовом расширения easyPrint для String.
Листинг 18.3. Расширение Any (Extensions.kt)
Запустите Extensions.kt и убедитесь, что вывод не изменился.
Так как вы добавили расширение для типа Any, оно доступно и для подтипов. Добавьте вызов расширения для Int.
Листинг 18.4. easyPrint доступно для всех подтипов (Extensions.kt)
А что, если вы хотите вывести строку «Madrigal has left the building» до и после вызова addEnthusiasm?
Для этого нужно добавить в функцию easyPrint возможность вызова в цепочке. Вы уже видели цепочки из вызовов функций: функции могут участвовать в цепочке, если они возвращают объект-приемник или иной объект, для которого можно вызывать последующие функции.
Обновите easyPrint для вызова цепочкой.
Листинг 18.5. Изменение easyPrint для вызова цепочкой (Extensions.kt)
Теперь попробуйте вызвать функцию easyPrint дважды: до и после addEnthusiasm.
Листинг 18.6. Вызов easyPrint дважды (Extensions.kt)
Код не скомпилировался. Первый вызов easyPrint был разрешен, а addEnthusiasm нет. Посмотрите на информацию о типе, чтобы понять, почему так происходит: щелкните на easyPrint и нажмите Control-Shift-P (Ctrl-P) и из появившегося списка расширений выберите первое: («Madrigal has left the building».easyPrint()") (рис. 18.1).
Функция easyPrint возвращает строку, для которой была вызвана, но для ее представления использует тип Any. addEnthusiasm доступна только для String, поэтому ее нельзя вызвать для значения, возвращаемого функцией easyPrint.
Чтобы решить эту проблему, можно сделать обобщенное расширение. Обновите функцию-расширение easyPrint и используйте обобщенный тип в качестве принимающего вместо Any.
Листинг 18.7. Обобщение easyPrint (Extensions.kt)
Теперь, когда расширение использует параметр обобщенного типа Т в качестве приемника и возвращает Т вместо Any, информация о конкретном типе объекта-приемника передается далее по цепочке (рис. 18.2).
Попробуйте выполнить Extensions.kt снова. В этот раз строка будет выведена дважды:
Ваша новая обобщенная функция-расширение работает с любым типом, а также обрабатывает информацию о нем. Расширения, использующие обобщенные типы, позволяют писать функции, которые могут работать с самыми разными типами в программе.
Расширения для обобщенных типов имеются и в стандартной библиотеке Kotlin. Например, посмотрите на объявление функции let:
let объявлена как обобщенная функция расширения, что позволяет ей работать со всеми типами. Она принимает лямбду, которая получает объект-приемник в качестве аргумента (Т) и возвращает значение некоторого нового типа R.
Обратите внимание на ключевое слово inline, о котором узнали в главе 5. Тот же совет, что мы давали раньше, применим и здесь: объявление функции-расширения встраиваемой, если она принимает лямбду, уменьшает затраты памяти.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Kotlin
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Неважно, опытный вы разработчик, который хочет выйти за рамки Java, или изучаете первый язык программирования. Джош и Дэвид проведут вас от основных принципов к расширенному использованию Kotlin, чтобы вы могли создавать надежные и эффективные приложения.
Для кого эта книга?
Мы (авторы) написали эту книгу для разработчиков разного калибра: опытных Android-разработчиков, которым не хватает возможностей Java, разработчиков серверного кода, заинтересованных в возможностях Kotlin, а также для новичков, решившихся на изучение эффективного компилируемого языка.
Эта книга может заинтересовать вас поддержкой Android, но она не ограничивается программированием на Kotlin для Android. Более того, в этой книге лишь одна глава — глава 21 — рассматривает приемы программирования на Kotlin для Android. Тем не менее если вас интересует тема использования Kotlin для разработки Android-приложений, эта книга познакомит вас с основными приемами, которые упростят процесс написания Android-приложений на Kotlin.
Несмотря на то что Kotlin подвергся влиянию некоторых других языков, вам ни к чему знать, как они устроены, чтобы успешно работать с Kotlin. Время от времени мы будем сравнивать код на Java и Kotlin. Если у вас есть опыт разработки в Java, это поможет вам понять связь между этими двумя языками. А если у вас нет такого опыта, примеры решения тех же задач на другом языке помогут вам понять идеи, повлиявшие на формирование Kotlin.
Как пользоваться этой книгой
Эта книга не является справочником. Наша цель — последовательное обучение языку Kotlin. Вы будете работать с проектами и изучать язык в процессе работы. Для большего эффекта мы рекомендуем опробовать все примеры кода по ходу чтения книги. Работа с примерами поможет вам развить «мышечную» память и даст понимание, позволяющее переходить от одной главы к другой.
Каждая следующая глава основывается на предыдущей. Мы рекомендуем не пропускать главы. Даже если вы изучили тему, работая с другими языками, мы предлагаем хотя бы прочитать об этом здесь: многое в Kotlin реализовано иначе. Мы начнем с вводных тем, таких как переменные и списки, а затем перейдем к приемам объектно-ориентированного и функционального программирования, чтобы дать вам понять, что делает Kotlin таким мощным инструментом. К концу книги вы пройдете путь от новичка до продвинутого разработчика на Kotlin.
Хотим добавить, что спешить не стоит: развивайтесь, используйте документацию Kotlin по ссылке: kotlinlang.org/docs/reference, где есть ответы на многие возникающие в ходе экспериментов вопросы.
Отрывок. Расширения
Расширения позволяют добавить функциональности типу без явного изменения объявления типа. Применяйте расширения с пользовательскими типами, а также с типами, над которыми у вас нет контроля, например List, String и другими типами из стандартной библиотеки Kotlin.
Расширения служат альтернативой наследованию. Они хорошо подходят для добавления функциональности в тип, если определение класса вам недоступно или класс не имеет модификатора open, позволяющего создавать подклассы.
Стандартная библиотека языка Kotlin часто использует расширения. Например, стандартные функции, которые вы изучили в главе 9, объявлены как расширения, и в этой главе вы увидите несколько примеров их объявления.
В этой главе мы сначала поработаем в проекте Sandbox, а затем применим полученные знания для оптимизации кода NyetHack. Начнем с того, что откроем проект Sandbox и создадим новый файл с именем Extensions.kt.
Объявление функции-расширения
Ваше первое расширение позволяет добавить любую степень восторга в String. Объявите его в Extensions.kt.
Листинг 18.1. Добавление расширения для типа String (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
Функции-расширения объявляются тем же способом, что и другие функции, но с одним отличием: определяя функцию расширения, вы также указываете тип, известный как принимающий тип, которому расширение добавляет возможностей. (Вспомните главу 9, где расширяемые типы мы называли «приемниками».) Для функции addEnthusiasm указан принимающий тип String.
Тело функции addEnthusiasm — это всего лишь одно выражение, которое возвращает строку: содержимое this и 1 или более восклицательных знаков, в зависимости от значения аргумента amount (1 — это значение по умолчанию). Ключевое слово this ссылается на экземпляр объекта-приемника, для которого вызвано расширение (в этом случае экземпляр String).
Теперь можно вызвать функцию addEnthusiasm для любого экземпляра String. Испытайте новую функцию-расширение, объявив строку в функции main и вызвав для нее функцию-расширение addEnthusiasm с выводом результата.
Листинг 18.2. Вызов нового расширения для экземпляра объекта-приемника String (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun main(args: Array<String>) {
println("Madrigal has left the building".addEnthusiasm())
}
Запустите Extensions.kt и посмотрите, добавляет ли функция-расширение восклицательный знак в строку, как и задумывалось.
Можно ли создать подкласс String, чтобы добавить эту возможность экземплярам String? В IntelliJ посмотрите на исходный код объявления String, нажав клавишу Shift дважды для открытия диалогового окна Search Everywhere, и введите в поле для поиска «String.kt». Вы увидите такое объявление класса:
public class String : Comparable<String>, CharSequence {
...
}
Так как ключевое слово open отсутствует в объявлении класса String, вы не сможете создать подкласс String, чтобы добавить новые возможности через наследование. Как ранее упоминалось, расширения — хороший вариант, если вы хотите добавить функциональности в класс, которым не можете управлять или который нельзя использовать для создания подкласса.
Объявление расширения для суперкласса
Расширения не полагаются на наследование, но их можно сочетать с наследованием для увеличения области видимости. Попробуйте сделать это в Extensions.kt: объявите расширение для типа Any с именем easyPrint. Так как расширение объявлено для Any, оно будет доступно для всех типов. В main замените вызов функции println вызовом расширения easyPrint для String.
Листинг 18.3. Расширение Any (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun Any.easyPrint() = println(this)
fun main(args: Array<String>) {
println("Madrigal has left the building".addEnthusiasm()).easyPrint()
}
Запустите Extensions.kt и убедитесь, что вывод не изменился.
Так как вы добавили расширение для типа Any, оно доступно и для подтипов. Добавьте вызов расширения для Int.
Листинг 18.4. easyPrint доступно для всех подтипов (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun Any.easyPrint() = println(this)
fun main(args: Array<String>) {
"Madrigal has left the building".addEnthusiasm().easyPrint()
42.easyPrint()
}
Обобщенные функции-расширения
А что, если вы хотите вывести строку «Madrigal has left the building» до и после вызова addEnthusiasm?
Для этого нужно добавить в функцию easyPrint возможность вызова в цепочке. Вы уже видели цепочки из вызовов функций: функции могут участвовать в цепочке, если они возвращают объект-приемник или иной объект, для которого можно вызывать последующие функции.
Обновите easyPrint для вызова цепочкой.
Листинг 18.5. Изменение easyPrint для вызова цепочкой (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun Any.easyPrint()= println(this): Any {
println(this)
return this
}
...
Теперь попробуйте вызвать функцию easyPrint дважды: до и после addEnthusiasm.
Листинг 18.6. Вызов easyPrint дважды (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun Any.easyPrint(): Any {
println(this)
return this
}
fun main(args: Array<String>) {
"Madrigal has left the building".easyPrint().addEnthusiasm().easyPrint()
42.easyPrint()
}
Код не скомпилировался. Первый вызов easyPrint был разрешен, а addEnthusiasm нет. Посмотрите на информацию о типе, чтобы понять, почему так происходит: щелкните на easyPrint и нажмите Control-Shift-P (Ctrl-P) и из появившегося списка расширений выберите первое: («Madrigal has left the building».easyPrint()") (рис. 18.1).
Функция easyPrint возвращает строку, для которой была вызвана, но для ее представления использует тип Any. addEnthusiasm доступна только для String, поэтому ее нельзя вызвать для значения, возвращаемого функцией easyPrint.
Чтобы решить эту проблему, можно сделать обобщенное расширение. Обновите функцию-расширение easyPrint и используйте обобщенный тип в качестве принимающего вместо Any.
Листинг 18.7. Обобщение easyPrint (Extensions.kt)
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun <T> AnyT.easyPrint(): AnyT {
println(this)
return this
}
...
Теперь, когда расширение использует параметр обобщенного типа Т в качестве приемника и возвращает Т вместо Any, информация о конкретном типе объекта-приемника передается далее по цепочке (рис. 18.2).
Попробуйте выполнить Extensions.kt снова. В этот раз строка будет выведена дважды:
Madrigal has left the building
Madrigal has left the building!
42
Ваша новая обобщенная функция-расширение работает с любым типом, а также обрабатывает информацию о нем. Расширения, использующие обобщенные типы, позволяют писать функции, которые могут работать с самыми разными типами в программе.
Расширения для обобщенных типов имеются и в стандартной библиотеке Kotlin. Например, посмотрите на объявление функции let:
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
let объявлена как обобщенная функция расширения, что позволяет ей работать со всеми типами. Она принимает лямбду, которая получает объект-приемник в качестве аргумента (Т) и возвращает значение некоторого нового типа R.
Обратите внимание на ключевое слово inline, о котором узнали в главе 5. Тот же совет, что мы давали раньше, применим и здесь: объявление функции-расширения встраиваемой, если она принимает лямбду, уменьшает затраты памяти.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Kotlin
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Комментарии (11)
PikselNsk
19.08.2019 23:37+1«Android. Программирование для профессионалов.» от Big Nerd Ranch в данный момент является топ-1 по моему скромному мнению, что есть из книг на русском языке по андроид. Надеюсь, что и в этой книге ребята смогут совместить прежний подход с концентрацией полезного материала. Приобрёл.
valery1707
Только в приведённом отрывке я нашёл две ошибки. Причём для обнаружения одной из них достаточно просто внимательно следить за повествованием.
Обидно что книга позиционируется "для профессионалов", но содержит такие ошибки.
Ошибки компиляции:
println("Madrigal has left the building".addEnthusiasm()).easyPrint()
println
не возвращает ничего, а значит иeasyPrint()
вызвать не получится.До этого писали что будет замена
println()
в пользуeasyPrint()
, но тут совместили оба варианта и в результате оно просто не компилируетсяfun <T> AnyT.easyPrint(): AnyT {
Нет типа
AnyT
, так какT
может быть любым идентификатором (многосимвольным, с любым кейсом символов и т.д.).Правильный вариант: указать
T
вместоAnyT
. О чём, кстати, написано в следующем абзаце.ilmirus
Первая «ошибка»:
УМВР:
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount)
fun Any.easyPrint() = println(this)
fun main() {
println("Madrigal has left the building".addEnthusiasm()).easyPrint()
}
выдает
Madrigal has left the building!
kotlin.Unit
Так что насчет «не компилируется» вы ошиблись.
valery1707
Согласен, оно компилируется, но при этом это всё равно ошибка:
При проверке у меня вот такое сообщение, но не помню что именно было в коде: