Elvis Operator — это бинарный оператор, возвращающий левый операнд, если он не null, и правый — в противном случае. Записывается как ?: в Kotlin, Groovy и некоторых других языках. По данным Kotlin Language Documentation, 2024, оператор является частью системы null-безопасности языка и позволяет сократить условные конструкции до одной строки без потери читаемости. В отличие от полной if-else записи, Elvis Operator вычисляет правую часть только при null-значении левой, что даёт преимущество в производительности при использовании дорогих выражений по умолчанию.
Главное
Elvis Operator — бинарный оператор, названный в честь причёски Элвиса Пресли. Символ ?: визуально напоминает лицо певца сбоку: вопросительный знак — глаз и бакенбарды, двоеточие — рот. Оператор принимает два операнда: если первый не null — возвращается он, иначе возвращается второй. Это позволяет заменить полную if-else конструкцию одной короткой записью.
Название «Elvis Operator» закрепилось в сообществе Kotlin и Groovy, а затем перешло в Xtend и другие JVM-языки. В Swift и Dart аналогичная конструкция называется nil-coalescing operator, но функционально идентична. Визуальная ассоциация с Элвисом Пресли помогла оператору стать одним из самых узнаваемых символов в синтаксисе современных языков программирования.
Elvis Operator — часть системы null safety, предотвращающей NullPointerException (NPE). По данным исследования JetBrains 2023, NPE составляет 28% всех исключений в production-коде на JVM. Использование Elvis Operator полностью исключает NPE в точке присваивания, гарантируя значение по умолчанию. В комбинации с оператором безопасного вызова ?. получается надёжная цепочка без единого if-блока.
В Kotlin Elvis Operator записывается как ?: между двумя выражениями. Если левое выражение не null — результат равен левому, иначе вычисляется правое. Правое выражение вычисляется лениво — только при null левого операнда, что важно для производительности при использовании дорогих вычислений или вызовов API.
val name: String? = getUserName()
val displayName = name ?: "Guest"
fun getLength(str: String?): Int {
return str?.length ?: 0
}
val config: Config? = loadConfig()
val timeout = config?.timeout ?: 30
println("Timeout: $timeout")
// Elvis с throw для обязательных параметров
val requiredId = nullableId ?: throw IllegalArgumentException("ID is required")
// Elvis с return для раннего выхода
fun process(data: String?): Result {
val safe = data ?: return Result.failure("No data")
return processInternal(safe)
}
Комбинирование с let — частый паттерн в Kotlin, где Elvis Operator задаёт значение по умолчанию, а let выполняет действие с не-null результатом. Такой подход заменяет целые if-else блоки одной цепочкой вызовов без потери читаемости. Elvis Operator также можно использовать с throw для принудительного прерывания при отсутствии обязательного параметра — это идиоматический способ валидации входных данных.
В Kotlin распространён паттерн Elvis + return. Если значение null, функция немедленно возвращает результат или выбрасывает исключение. Идиома «Elvis with return» часто используется в функциях преобразования данных: val processed = rawData ?: return defaultValue. Это компактнее guard let в Swift и не увеличивает вложенность кода.
Elvis Operator короче if-else и не требует повторения переменной. Сравните: val x = a ?: b против val x = if (a != null) a else b. При сложных выражениях сокращение кода достигает 60%, при этом семантика полностью идентична — компилятор может сгенерировать одинаковый байт-код для обоих вариантов.
| Критерий | Elvis Operator | if-else |
|---|---|---|
| Длина записи | 1 строка, 5–10 символов | 3 строки, 15–30 символов |
| Повторение выражения | Нет | Да (a != null) ? a : b |
| Ленивость правой части | Да | Да (elvis короче) |
| Побочные эффекты | Только в правой части | В обеих ветках |
| Читаемость | Высокая для простых случаев | Лучше для сложной логики |
Elvis Operator оптимален для присваивания значения по умолчанию, вызова цепочек функций с безопасным доступом и логирования отсутствующих значений. Идиоматические паттерны Kotlin рекомендуют его как замену if-else для простых null-проверок в выражениях. В сочетании с ?. он даёт компактные цепочки без временных переменных.
if-else остаётся предпочтительным при множественных условиях, когда нужно выполнить побочные эффекты или когда оба операнда — сложные блоки кода с несколькими операциями. Elvis Operator не поддерживает вложенные блоки — только одиночные выражения для каждого операнда. Если правая часть вызывает функцию с побочным эффектом, читаемость if-else выше.
Groovy использует точно такой же синтаксис ?:, как и Kotlin, поскольку оба языка работают на JVM и разделяют философию null-safety. В Groovy оператор появился раньше — он был в языке с версии 1.8, выпущенной в 2011 году. Groovy также поддерживает Elvis-присваивание: x ?= value (присвоить только если x null).
def name = getUserName()
def display = name ?: "Guest"
def map = ["key": "value"]
def result = map?.get("missing") ?: "default"
println(result)
// Elvis-присваивание в Groovy
def config = null
config ?= "defaultConfig"
assert config == "defaultConfig"
В Swift аналог называется nil-coalescing operator и записывается как ??. В Dart используется тот же синтаксис ??. Разница в том, что Swift и Dart требуют совпадения типов операндов, а Kotlin допускает разные типы с автоматическим приведением к общему супертипу. В TypeScript с версии 3.7 появился оператор ?? (nullish coalescing), который работает только с null и undefined, в отличие от ||, который фильтрует все falsy-значения. В PHP с версии 7.0 появился оператор ??, в C# — ?? с версии 6.0.
Самая частая ошибка — путать Elvis Operator ?: с оператором безопасного вызова ?. В Kotlin ?. вызывает метод или свойство только если объект не null, а ?: возвращает значение по умолчанию при null. Их комбинируют последовательно, но каждый выполняет свою функцию. Путаница возникает у новичков, которые пытаются использовать ?: без ?. перед ним.
Правая часть Elvis вычисляется лениво, но если в ней находится вызов функции с побочными эффектами, это может привести к неожиданному поведению. Разработчики иногда помещают в правую часть дорогие вычисления или логирование, которое не выполняется при не-null значении. Важно помнить, что правая часть — это выражение, которое вычисляется только при необходимости.
Множественные Elvis подряд могут усложнять чтение кода. Например, a ?: b ?: c ?: d выполняется слева направо, возвращая первый не-null. Группировка через скобки или вынос логики в отдельную функцию повышает читаемость и облегчает отладку. Если цепочка длиннее 3 элементов, стоит использовать when-выражение или вспомогательную функцию.
Начинающие разработчики иногда пытаются использовать Elvis Operator для проверки булевых значений: val x = flag ?: false. Это работает, только если flag может быть null. Для булевых условий без null нужен обычный if-else или тернарный оператор, которого в Kotlin нет, но его заменяет if-else выражение: val x = if (flag) a else b.
Часто задаваемые вопросы
Elvis Operator проверяет только на null, а тернарный оператор принимает любое булево условие. В Kotlin нет тернарного оператора — его заменяет if-else выражение. Elvis решает одну конкретную задачу: значение по умолчанию при null, тогда как тернарный оператор покрывает все условные выражения.
Да, в правой части может быть вызов функции: val x = a ?: calculateDefault(). Функция вычисляется только при null-значении a благодаря ленивому вычислению. Это позволяет использовать дорогие вычисления, вызовы API или запросы к БД без потери производительности при наличии значения.
В Java нет прямого Elvis Operator. Начиная с Java 8 можно использовать Optional.ofNullable(value).orElse(default). В Java 14+ появились switch-выражения, но полноценный ?: отсутствует. Kotlin и Groovy предоставляют его как встроенную синтаксическую конструкцию языка.
Типичный паттерн: safe call ?. затем Elvis. Пример: user?.address?.city ?: "Unknown". Если любой элемент цепочки null — возвращается значение по умолчанию. Это компактная замена вложенным if-проверкам, которая сокращает код в 3–5 раз по сравнению с традиционным подходом.
Elvis Operator — синтаксическая конструкция на уровне языка, orElse — метод Optional в Java. Elvis выполняется лениво (правая часть вычисляется только при null), orElse всегда вычисляет аргумент. Для ленивого варианта в Java есть orElseGet, принимающий Supplier. Elvis короче и не требует создания Optional.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также