Шаг 238.
Основы Kotlin.
Совместимость с Java. За пределами класса (продолжение)

    На этом шаге мы рассмотрим различия в реализации перегрузки в Java и Kotlin.

    Еще одна важная аннотация JVM - @JvmOverloads. Параметры по умолчанию языка Kotlin дают возможность использовать более простой подход взамен громоздкого, многократного объявления перегруженных версий метода. Что это означает на практике? Следующий пример должен дать вам ответ на этот вопрос.

    Добавьте новую функцию с именем handOverFood() в Hero.kt.

.   .   .   .   .
fun makeProclamation() = "Greetings, beast!"

fun handOverFood(leftHand: String = "berries", rightHand: String = "beef") {
    println("Mmmm... you hand over some delicious $leftHand and $rightHand.")
}


Рис.1. Добавление функции с параметрами по умолчанию (Hero.kt)

    Герой предлагает кое-что из еды в функции handOverFood(), и вызывающий ее код может выбирать, какую еду он будет держать в правой, а какую в левой руке, или оставить выбор по умолчанию - мясо и ягоды (beef и berries). Kotlin позволяет организовать такую возможность выбора, не усложняя код.

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

public static void handOverFood(String leftHand, String rightHand) {
  System.out.println("Mmmm... you hand over some delicious " +
   leftHand + " and " + rightHand + ".");
}

public static void handOverFood(String leftHand) {
  handOverFood(leftHand, "beef");
}

public static void handOverFood() {
  handOverFood("berries", "beef");
}

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

  handOverFood(rightHand="cookies")
выведет результат
  "Mmmm... you hand over some delicious berries and cookies"    . 
Java не поддерживает именованные аргументы и поэтому не различает методы, вызываемые с одинаковым числом параметров.

    Как вы вскоре увидите, аннотация @JvmOverloads обеспечивает автоматическое создание трех соответствующих методов, поэтому пользователи Java не сильно ущемлены.

    Монстр Jhava ненавидит фрукты. Ему больше понравились бы пицца или стейк, а не ягоды. Добавьте метод с именем offerFood() в Jhava.java, который дает герою возможность предложить еду монстру.

.    .    .    .
    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }

    public void offerFood() {
        Hero.handOverFood("pizza");
    }
}


Рис.2. Запись всего лишь одного метода (Jhava.java)

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

public static final void handOverFood(@NotNull String leftHand,
   @NotNull String rightHand) {
      Intrinsics.checkParameterIsNotNull(leftHand, "leftHand");
      Intrisics.checkParameterIsNotNull(rightHand, "rightHand");
      String var2 = "Mmmm... you hand over some delicious " +
          leftHand + " and " + rightHand + '.';
      System.out.println(var2);
}

    В Kotlin есть способы избежать перегрузки методов, а в Java такая роскошь отсутствует. Аннотация @JvmOverloads поможет пользователям вашего API в Java, предоставив перегруженные версии функций Kotlin. Добавьте аннотацию для handOverFood() в Hero.kt.

.   .   .   .   .
fun makeProclamation() = "Greetings, beast!"

@JvmOverloads
fun handOverFood(leftHand: String = "berries", rightHand: String = "beef") {
    println("Mmmm... you hand over some delicious $leftHand and $rightHand.")
}


Рис.3. Добавление @JvmOverloads (Hero.kt)

    Вызов handOverFood() в Jhava.offerFood больше не приводит к ошибке, так как теперь происходит вызов версии handOverFood(), существующей в Java. В этом можно убедиться, если посмотреть на скомпилированный байт-код Java:

@JvmOverloads
public static final void handOverFood(@NotNull String leftHand,
   @NotNull String rightHand) {
      Intrinsics.checkParameterIsNotNull(leftHand, "leftHand");
      Intrisics.checkParameterIsNotNull(rightHand, "rightHand");
      String var2 = "Mmmm... you hand over some delicious " +
          leftHand + " and " + rightHand + '.';
      System.out.println(var2);
}
@JvmOverloads
public static final void handOverFood(@NotNull String leftHand) {
      handOverFood$default(leftHand, (String)null, 2, (Object)null);
}
@JvmOverloads
public static final void handOverFood() {
      handOverFood$default((String)null, (String)null, 3, (Object)null);
}

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

    Чтобы проверить, как работает передача еды монстру, вызовем offerFood() в Hero.kt.

@file:JvmName("Hero")

fun main(args: Array<String>) {
    .   .   .   .
    adversary.offerFood()
}

fun makeProclamation() = "Greetings, beast!"
Файл с проектом можно взять здесь.


Рис.4. Тестируем offerFood() (Hero.kt)

    Запустите Hero.kt и убедитесь, что герой передал пиццу и стейк.


Рис.5. Результат работы приложения

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг