Site icon AppTractor

Красота системы типов Kotlin

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

Any

Аналогичен классу Object в Java, который является корневым типом каждого объекта, но между ними есть тонкие различия. Как следует из именования в Java, он работает только с объектными типами, но не с примитивными типами, такими как int, char и т.д. Конечно, это связано с некоторым низкоуровневым управлением памятью и тем, где должна храниться переменная, и мы можем получить больше производительности, если будем использовать только примативные типы. Но Kotlin лучше работает даже здесь, он автоматически преобразует к примитивным типам Java, где это возможно, и устраняет необходимость объявлять примитивные типы специально с точки зрения языка программирования высокого уровня. Это решение делает его более последовательным, а также отделяет его от реализации Java, чтобы впоследствии использовать Kotlin на множестве платформ.

Java Object в сравнении с Kotlin Any

 

public open class Any {
    public open operator fun equals(other: Any?): Boolean
    public open fun hashCode(): Int
    public open fun toString(): String
}

Когда дело доходит до специфических для Java реализаций, от которых Kotlin не может избавиться, он иногда использует функции расширения Kotlin и помещает эти расширения в отдельный пакет. Вы можете проверить, как функция Java getClass() реализована в Kotlin, а также проверить, насколько она проста/элегантна, как определение Any в Kotlin выше.

Optional

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

Ответ на этот вопрос также довольно распространен в наши дни — Nullability. Каждый тип в Kotlin будет иметь версию nulltable, и, объявив nullability специально, мы можем быть уверены в том, что нам нужно или не нужно делать проверку на null, и навсегда избавиться от NPE благодаря компилятору Kotlin.

Есть один тонкий фактор, который мы используем каждый день, но о котором, возможно, не знаем. Типы NonNull и Nullable принадлежат к разным группам, и только тип Nullable может перейти к типу NonNull, доказав свою нулевость, неважно, с помощью проверки на нулевость или принудительного разворачивания. Таким образом, каждый NonNull/Nullable тип остается в своей собственной группе постоянно, что обеспечивает безопасность.

Хотя в собственном мире Kotlin работать с null безопасно, вам может быть интересно узнать, как использовать Kotlin вместе с Java. За дополнительной информацией обращайтесь на сайт с официальной документацией.

Nothing

А вот и самая интересная часть системы типов Kotlin с моей точки зрения — Nothing. Давайте сначала посмотрим исходный код:

public class Nothing private constructor()

Даже если это всего лишь одна строчка кода, она говорит о многом.

Казалось бы, не самый полезный тип, верно? Даже если вы не разбираетесь в Nothing, вы можете без проблем написать код без ошибок. Однако если вы углубитесь в документацию или комментарии к Nothing, то вскоре обнаружите, что он функционирует как нижний тип для всех остальных типов, противоположный Any.

Тип Nothing

Зачем нам нужен нижний тип для чего-либо?Разве он не будет подключен, если он является дочерним типом String и Int? Да, пересечение всего — это то, что не может существовать на самом деле, и это как раз и есть определение Nothing.

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

interface Animal {
    fun eat(): String

    fun walk(): String
}

class Dog : Animal {
    override fun eat() = "dog eat"

    override fun walk() = "dog walk"
}

Давайте определим тип Animal с двумя классическими функциями, обе из которых возвращают строку для отображения действия. Что, если мы хотим создать Fish, который является продолжением Animal?

class Fish : Animal {
    override fun eat() = "fish eat"

    override fun walk() = throw RuntimeException("fish can't walk")
}

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

fun main() {
    val fish = Fish()
    fish.walk()
    println("where am I") // IDE warn this will be unreachable code
}

И если вы назначите тип рыбы для Animal, предупреждение исчезнет. Если вы проверите возвращаемый тип рыбной функции walk, вы обнаружите, что он автоматически меняется со String на Nothing. Поскольку Nothing не может существовать, функция с типом возвращаемого значения Nothing не может успешно вернуть значение, поэтому IDE может знать, что println недоступен. И поскольку throw может произойти где угодно, у каждого типа будет дочерний тип Nothing. Следовательно, IDE может работать лучше, а сам язык может иметь лучший дизайн, который является более последовательным.

Nothing не ограничивает возможность генерирования исключений, функция с бесконечным циклом также является допустимым случаем. Функция TODO() также ничего не вернет, поскольку она не должна быть успешно выполнена. И если вы поищете использование Nothing в самом Kotlin, вы, возможно, обнаружите, что тип также используется для представления любой пустой коллекции, поскольку Nothing может быть общим дочерним элементом для всего. Хотя на самом деле вы не можете его использовать, но оно служит хорошим заполнителем, который вы можете заменить позже. Возьмем, к примеру, listOf:

var list: List<String> = listOf()

// Kotlin's Collections.kt
public inline fun <T> listOf(): List<T> = emptyList()

public fun <T> emptyList(): List<T> = EmptyList

internal object EmptyList : List<Nothing>

Бонус

Можете ли вы попытаться определить, к какому именно типу относится myObj, выведенный ниже? (Ответ: можно вставить в IDE и попытаться переназначить его):

var myObj = null

На сегодня это все. С Any в качестве верхнего типа, Nothing в качестве нижнего типа и Optional в стороне, я надеюсь, что вы так же найдете систему типов Kotlin приятной и красивой.

Источник

Exit mobile version