Когда мы вызываем функцию, может быть или не быть аргумента, который нам нужно передать. Допустим, у нас есть объект person.
data class Person(val name: String, val height: Int, val weight: Int)
Затем предположим, что мы собираемся проверить, есть ли у человека лишний вес, с помощью метода
fun isOverweight(weight: Int): Boolean
затем мы можем использовать этот метод, написав
val person = Person("John Doe", 170, 90) val isOverweight = isOverweight(person.weight)
В этом нет ничего плохого. Но, как видите, метод isOverweight
принимает Int
. Это Int
может быть чем угодно. Вы даже можете передать этой функции рост человека, и она будет работать нормально.
val isOverweight = isOverweight(person.height)
Теперь это может быть проблемой для нас. К счастью, в Kotlin мы можем попытаться свести это к минимуму.
Kotlin Именованный параметр / Именованный аргумент
Давайте попробуем переписать приведенный выше код с именованным аргументом.
val person = Person(name = "John Doe", height = 170, weight = 90) val isOverweight = isOverweight(weight = person.weight)
Можно возразить, что особой разницы нет. Но лично для меня это повышает читабельность моего кода и снижает вероятность того, что я передам height
моему методу isOverweight
.
Но, конечно, должен быть лучший способ решить эту проблему.
Класс-оболочка
Мы можем попытаться обернуть значение в класс, чтобы оно представляло более конкретный тип. Если мы перепишем код, используя класс-оболочку, он будет выглядеть так.
data class Weight(val value: Int) data class Height(val value: Int) val person = Person("John Doe", Height(170), Weight(90)) val isOverweight = isOverweight(person.weight) fun isOverweight(weight: Weight): Boolean
Используя этот подход, мы можем гарантировать, что не сможем передать person.height
в качестве аргумента функции isOverweight
. Это решение действительно аккуратное, и это определенно то, что нам нужно.
Но тогда можно подумать, что не стоит создавать класс для переноса одного примитивного значения только для того, чтобы мы могли избежать передачи неправильного значения. Кроме того, это вводит проблему производительности.
Начиная с Kotlin 1.3, есть способ сделать это, не влияя на производительность нашего приложения.
Встроенный класс
Встроенный класс — это способ обернуть значение без увеличения производительности. Способ его использования такой.
inline class Weight(val value: Int) inline class Height(val value: Int) val person = Person("John Doe", Height(170), Weight(90)) val isOverweight = isOverweight(person.weight) fun isOverweight(weight: Weight): Boolean
По сути, это тот же код, что и в подходе класса-оболочки, но мы изменили оболочку data class
для height
и weight
на inline class
, чтобы устранить накладные расходы на производительность.
Встроенный класс — это оболочка, которая может иметь только один член. Этот элемент является значением, которое вы хотите обернуть.
Когда он скомпилирован, он будет просто представлен как обернутое значение. Например, вы можете увидеть этот байт-код Java для класса Person.
public final class Person { @NotNull private final String name; private final int height; private final int weight; //... }
Вы можете видеть, что высота и вес будут представлены как обычная JVM int
. Вот почему inline class
отличается от обычного класса-оболочки. Хотя в некоторых случаях он все же будет представлен как тип оболочки. Подробнее можно прочитать на этой странице.
И поскольку он предназначен для использования в качестве класса-оболочки, существуют ограничения на то, что он может делать по сравнению с обычным классом.
В настоящее время он все еще является частью экспериментальной функции Kotlin.
В заключение, есть несколько способов свести к минимуму ошибку передачи значения того же типа, что и аргумент. В Kotlin (начиная с Kotlin 1.3) лучше всего использовать inline class
, поскольку он действительно создан для этой цели. Несмотря на это, есть некоторые вещи, которые необходимо учитывать при его использовании. Для получения дополнительной информации, пожалуйста, посмотрите официальную документацию на сайте Kotlin.