Как вернуть несколько значений из функции в Kotlin (аналог Swift)
Использование Pair, Triple и data-классов
В Kotlin нет встроенной поддержки именованных кортежей, как в Swift, но есть несколько идиоматических способов вернуть несколько значений. Лучший подход зависит от количества возвращаемых значений и контекста.
1. Pair и Triple (для 2-3 значений)
Если вам нужно вернуть ровно 2 или 3 значения, используйте встроенные классы Pair и Triple. Они поддерживают деструктуризацию, что делает код чистым:
fun getUser(): Pair<String, Int> {
return Pair("Alice", 30)
}
fun main() {
val (name, age) = getUser()
println("Name: $name, Age: $age") // Name: Alice, Age: 30
}
Для трёх значений:
fun getCoordinates(): Triple<Double, Double, Double> {
return Triple(55.75, 37.62, 156.0)
}
fun main() {
val (lat, lon, altitude) = getCoordinates()
println("Lat: $lat, Lon: $lon, Alt: $altitude")
}
2. Data-классы (рекомендуемый способ для >3 значений)
Для возврата 4+ значений или если семантика важна, создайте data class. Это даёт именованные поля, автоматически генерирует toString(), equals(), copy() и поддерживает деструктуризацию:
data class User(val name: String, val age: Int, val email: String, val isActive: Boolean)
fun getUser(): User {
return User("Alice", 30, "alice@example.com", true)
}
fun main() {
val user = getUser()
println("${user.name}, ${user.age}, ${user.email}")
// Деструктуризация тоже работает:
val (name, age, email, isActive) = getUser()
println("Name: $name, Email: $email")
}
3. Использование стандартной библиотеки с компонентными функциями
Вы можете добавить свои componentN() функции к любому классу, чтобы включить деструктуризацию:
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
fun getPoint(): Point = Point(10, 20)
fun main() {
val (x, y) = getPoint()
println("x=$x, y=$y")
}
4. Функции-расширения для Pair/Triple
Если вам не нравятся .first и .second, добавьте читаемые имена через расширения:
fun Pair<String, Int>.toUser() = User(first, second) // преобразуем в data class
// Или напрямую:
val Pair<String, Int>.name: String get() = first
val Pair<String, Int>.age: Int get() = second
fun getUser(): Pair<String, Int> = "Alice" to 30
fun main() {
val user = getUser()
println("${user.name} is ${user.age} years old") // работает!
}
Сравнение со Swift
- Swift: именованные кортежи — встроенная лёгкая структура без объявления типа.
- Kotlin: используйте
data class— это почти то же самое, но требует объявления. Для 2-3 полей —Pair/Tripleс деструктуризацией.
Совет: Для любого кода, который будет использоваться в нескольких местах, всегда предпочитайте data class. Он делает код самодокументируемым и безопасным по типам. Pair/Triple хороши для быстрых локальных решений.
Альтернатива: лямбды с несколькими параметрами или sealed class
Если вам нужно вернуть разные типы результатов в зависимости от условий, рассмотрите sealed class (запечатанный класс). Это особенно полезно для обработки ошибок или различных состояний:
sealed class Result {
data class Success(val data: List<String>, val count: Int) : Result()
data class Error(val message: String, val code: Int) : Result()
}
fun fetchData(): Result {
return if (Math.random() > 0.5) {
Result.Success(listOf("a", "b"), 2)
} else {
Result.Error("Network error", 500)
}
}
fun main() {
when (val result = fetchData()) {
is Result.Success -> println("Data: ${result.data}, count: ${result.count}")
is Result.Error -> println("Error: ${result.message} (${result.code})")
}
}
Также можно использовать inline class (value class) для обёртки одного значения с семантикой, но это не даёт множественного возврата.
Ещё один приём — передать в функцию объект-получатель (receiver) и мутировать его поля, но это менее идиоматично для Kotlin.
Итог: Для простых случаев — Pair/Triple с деструктуризацией. Для сложных или семантически важных данных — data class. Для вариантов результата — sealed class. Избегайте возврата Any? или использования Array<Any> — это убивает типобезопасность.
Ответов пока нет.
Войдите чтобы оставить ответ