Шаг 233.
Основы Kotlin.
Совместимость с Java. Совместимость и null

    На этом шаге мы рассмотрим обработку пустых значений, возвращаемых из Java.

    Добавим еще один метод в Jhava с именем determineFriendshiplevel() (уровень дружелюбности). Метод determineFriendshiplevel() должен возвращать значение типа String, но, так как монстру чуждо понятие дружелюбия, он возвращает null.

public class Jhava {
    public String utterGreeting() {
        return "BLARGH";
    }

    public String determineFriendshipLevel() {
        return null;
    }
}


Рис.1. Возвращение null из метода Java (Jhava.java)

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

fun main(args: Array<String>) {
    val adversary = Jhava()
    println(adversary.utterGreeting())

    val friendshipLevel = adversary.determineFriendshipLevel()
    println(friendshipLevel.toLowerCase())
}


Рис.2. Вывод уровня дружелюбности (Hero.kt)

    Запустите Hero.kt. Хотя компилятор и не сообщил об ошибках, после запуска программа тут же завершится с ошибкой:

Exception in thread "main"
java.lang.IllegalStateException: friendshipLevel must not be null


Рис.3. Вывод сообщения об ошибке

    Мы уже говорили о том, что в Java все объекты могут быть null. Вызывая Java-метод, такой как determineFriendshiplevel(), мы видим, что он возвращает String, но это не значит, что возвращаемое значение будет соответствовать правилам языка Kotlin в отношении null.

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

    Удерживая клавишу Ctrl (Command), щелкните левой кнопкой мыши на determineFriendshiplevel() в Hero.kt. IntelliJ сообщит, что метод возвращает значение String!. Восклицательный знак говорит о том, что возвращаемое значение может быть String или String?. Компилятор Kotlin не знает, какое значение будет получено из Java, - строка или null.

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

    К счастью, программисты на Java могут писать дружелюбный к Kotlin код, явно сообщая о поддержке null с помощью соответствующих аннотаций. Объявим явно, что determineFriendshiplevel() может возвращать значение null, добавив аннотацию @Nullable в заголовок метода.

import org.jetbrains.annotations.Nullable;

public class Jhava {
    public String utterGreeting() {
        return "BLARGH";
    }

    @Nullable
    public String determineFriendshipLevel() {
        return null;
    }
}


Рис.4. Указание, что возвращаемое значение может быть null (Jhava.java)


Вам надо будет импортировать org.jetbrains.annotations.Nullable, который предложит вам IntelliJ.

    @Nullable предупреждает пользователя этого API, что метод может (а не обязан) вернуть null. Компилятор Kotlin понимает значение этой аннотации. Вернитесь в Hero.kt и обратите внимание, что теперь IntelliJ предупреждает вас о прямом вызове toLowerCase() для String?.

    Замените прямой вызов безопасным.

fun main(args: Array<String>) {
    val adversary = Jhava()
    println(adversary.utterGreeting())

    val friendshipLevel = adversary.determineFriendshipLevel()
    println(friendshipLevel?.toLowerCase())
}
Файл с проектом можно взять здесь.


Рис.5. Обработка null с помощью оператора безопасного вызова (Hero.kt)

    Запустите Hero.kt. Теперь значение null будет выведено в консоль.


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

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




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