[Программирование, Kotlin] Повышение читаемости кода с помощью расширений Kotlin (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
18-Дек-2020 19:31
В преддверии старта курса "Kotlin Backend Developer" приглашаем будущих студентов и всех желающих посмотреть открытый урок на тему "Пересмотр «12 факторов»: создаём современный микросервис на Kotlin".
Также делимся с вами традиционным переводом полезного материала.
Терминология Kotlin: функции-расширения и свойства-расширенияКогда вы использовали какой-либо API-интерфейс, хотелось ли вам добавить в него новые функции или свойства?Для решения этой задачи вы можете использовать наследование (создать новый класс на базе существующего) или функцию, которая получает в качестве входного параметра экземпляр класса. В языке программирования Java эта задача обычно решается с помощью класса Utils, но он не виден при использовании функции автозавершения кода, что затрудняет поиск и делает использование этого класса менее интуитивно понятным. Оба этих подхода можно использовать для решения нашей задачи, но ни один из них не дает понятный и хорошо читаемый код.К счастью, на помощь приходит Kotlin с функциями-расширениями и свойствами-расширениями. Они позволяют добавлять в класс нужный функционал без необходимости использовать наследование или создавать функцию, принимающую экземпляр класса в качестве параметра. В Android Studio эти расширения видны при использовании функции автозавершения кода, в отличие от соответствующего аналога в языке Java. Расширения можно использовать в сторонних библиотеках, Android SDK или пользовательских классах.Читайте дальше, если хотите узнать, как повысить читаемость вашего кода с помощью расширений!Использование функций-расширенийПредставим, что у вас есть класс Dog, описывающий собаку, у которой есть имя, порода и возраст.
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
data class Dog(val name: String, val breed: String, val age: Int)
Допустим, некий приют для животных хочет расширить класс Dog, чтобы в нем была функция, которая печатает информацию о собаке, если кто-то захочет забрать ее себе. Для этого мы реализуем функцию-расширение, которая объявляется как обычная функция, но с одной особенностью: перед именем функции добавляется имя расширяемого класса с точкой. В коде функции вы можете использовать служебное слово this для обращения к объекту-получателю, и у вас есть доступ ко всем членам класса-получателя в пределах функции.Вы можете вызвать функцию printDogInformation() так же, как вы вызываете любую другую функцию в классе Dog.
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
fun main() {
  val dog = Dog("Jen", "Pomeranian", 13)
  dog.printDogInformation()
}
Вызов функций-расширений из кода на языке JavaФункции-расширения не являются частью расширяемого класса, поэтому при попытке вызвать их из Java мы не найдем их среди методов расширяемого класса. Как мы увидим позже, расширения декомпилируются в статические методы файла, в котором вы их определили, и получают в качестве входного параметра экземпляр расширяемого класса. Вот как бы выглядел вызов функции-расширения printDogInformation() из Java:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
DogExtensionKt.printDogInformation(dog);
Функции-расширения для типов, допускающих неопределенное значениеРасширения можно также использовать для работы с типами, допускающими неопределенное значение (nullable). Вместо того чтобы делать проверку на null перед вызовом функции-расширения, мы можем создать функцию-расширение для nullable-типа и реализовать проверку на null в коде этой функции. Вот так будет выглядеть функция printInformation(), использующая тип, допускающий неопределенное значение.
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
fun Dog?.printInformation() {
  if (this == null){
    println("No dog found")
    return
  }
  println("Meet ${this.name} a ${this.age} year old ${this.breed}")
}
Как видите, не нужно делать проверку на null перед вызовом функции printInformation().
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
fun main() {
  val dog : Dog? = null
  dog.printInformation() // prints "No dog found"
}
Использование свойств-расширенийПредставим, что нашему приюту для животных также нужно знать, подходит ли собака по возрасту для передачи в новую семью. Для этого мы реализуем свойство-расширение isReadyToAdopt, которое будет показывать, превышает ли возраст собаки 1 год.
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
val Dog.isReadyToAdopt: Boolean
get() = this.age > 1
Вы можете обратиться к этому свойству-расширению так же, как вы обращаетесь к любому другому свойству в классе Dog.
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
fun main() {
  val dog1 = Dog("Jen", "Pomeranian", 13)
  if(dog1.isReadyToAdopt){
    print("${dog1.name} is ready to be adopted")
  }
}
Переопределение функций-расширенийНевозможно переопределить существующую функцию-член класса. Если определить функцию-расширение с такой же сигнатурой, что и у существующей функции-члена класса, то всегда будет вызываться функция-член, так как то, какая именно функция вызывается, зависит от объявленного статического типа переменной, а не от типа значения данной переменной во время выполнения кода. Например, нельзя расширить функцию toUppercase(), применяемую к строковому типу (String), но можно расширить функцию convertToUppercase().Следствие такого поведения можно увидеть, если попытаться расширить библиотечный тип, которым вы не владеете, когда владелец библиотеки добавил в библиотеку метод с той же сигнатурой, что и у вашего расширения. В этом случае будет вызываться библиотечное расширение, а вы получите только информацию о том, что ваша функция-расширение стала неиспользуемым методом.Внутреннее устройство расширенийМы можем декомпилировать функцию printDogInformation() в Android Studio. Для этого нужно выбрать в меню пункт Tools/Kotlin/Show Kotlin Bytecode (Инструменты/Kotlin/Показать байт-код Kotlin) и нажать кнопку Decompile (Декомпиляция). В декомпилированном виде метод printDogInformation() будет выглядеть так:
<!-- Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 -->
public static final void printDogInformation(@NotNull Dog $this$printDogInformation) {
  Intrinsics.checkParameterIsNotNull($this$printDogInformation, "$this$printDogInformation");
  String var1 = "Meet " + $this$printDogInformation.getName() + ", a " + $this$printDogInformation.getAge() + " year old " + $this$printDogInformation.getBreed();
  boolean var2 = false;
  System.out.println(var1);
}
В реальности функции-расширения являются обычными статическими функциями, которым в качестве входного параметра передается экземпляр класса-получателя. Они не имеют никакой другой связи с классами-получателями. Именно поэтому отсутствуют резервные поля — такие функции на самом деле не добавляют члены в класс.ЗаключениеВ целом расширения являются полезным инструментом, который следует использовать внимательно. Используя их, следуйте указанным ниже рекомендациям, и ваш код станет более понятным и читаемым.Что следует помнить:
  • Расширения преобразуются в статические функции.
  • Функциям-членам класса всегда отдается предпочтение.
  • Возьмите собаку из приюта!
Успехов в программировании!
ЗАБРАТЬ СКИДКУ
===========
Источник:
habr.com
===========

===========
Автор оригинала: Meghan Mehta
===========
Похожие новости: Теги для поиска: #_programmirovanie (Программирование), #_kotlin, #_kotlin, #_bekend (бэкенд), #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
)
, #_programmirovanie (
Программирование
)
, #_kotlin
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 13:45
Часовой пояс: UTC + 5