Оператор зірочки Kotlin перед ім'ям змінної або оператором поширення в Kotlin


97

Я хочу знати, що саме робить зірочка перед назвою змінної в Kotlin. Я бачив це ( *args) у прикладі Spring boot Kotlin :

@SpringBootApplication
open class Application {

    @Bean
    open fun init(repository: CustomerRepository) = CommandLineRunner {
        repository.save(Customer("Jack", "Bauer"))
        repository.save(Customer("Chloe", "O'Brian"))
        repository.save(Customer("Kim", "Bauer"))
        repository.save(Customer("David", "Palmer"))
        repository.save(Customer("Michelle", "Dessler"))
    }
}

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

Відповіді:


163

*Оператор відомий як спред оператора в Котлин.

З посилання на Kotlin ...

Коли ми викликаємо функцію vararg, ми можемо передавати аргументи по одному, наприклад, asList (1, 2, 3), або, якщо ми вже маємо масив і хочемо передати його вміст функції, ми використовуємо розповсюдження оператор (префікс масиву *):

Його можна застосувати до масиву перед передачею його у функцію, яка приймає varargs.

Наприклад...

Якщо у вас є функція, яка приймає різну кількість аргументів ...

fun sumOfNumbers(vararg numbers: Int): Int {
    return numbers.sum()
}

Ви можете передати в нього масив так ...

val numbers = intArrayOf(2, 3, 4)
val sum = sumOfNumbers(*numbers)
println(sum) // Prints '9'

Примітки:

  • *Оператор також оператор множення (звичайно).
  • Оператор можна використовувати лише при передачі аргументів функції. Результат операції не може бути збережений, оскільки він не дає значення (це суто синтаксичний цукор).
  • Спочатку оператор може заплутати деяких програмістів C / C ++, оскільки схоже на те, що вказівник не має посилань. Це не так; Котлін не має поняття вказівників .
  • Оператор може бути використаний між іншими аргументами при виклику функції vararg. Це продемонстровано на прикладі тут .
  • Оператор подібний до applyфункції в різних мовах функціонального програмування.

Чи є оператор Spread вбудованим масивом? Наприклад, для масиву a = [1, 2, 3] funWithVararg (* a) вбудовується у funWithVararg (1,2,3)? Я маю на увазі на рівні байт-коду.
Девід

22

На додаток до відповідей, які безпосередньо стосувались "що це за штука!?!", У вас часто трапляється той випадок, коли у вас є a, Listі ви хочете передати його функції, яка очікує a vararg. Для цього перетворення:

someFunc(x, y, *myList.toTypedArray())

Припускаючи, що останній параметр someFuncмає varargтой самий тип, що і елементи у списку.


Дуже дякую! Це повинно бути в офіційних документах у розділі оператора розповсюдження, як чогось, на що слід звертати увагу, коли ваш оператор розповсюдження не працює.
gladesktop

Дякую! Дійсно корисно. Цікаво, що таке "оператор розповсюдження" за лаштунками? Це просто спосіб отримати значення varargs?
Nicolas Jafelle

11

Як описано в документації, це оператор поширення:

Коли ми викликаємо функцію vararg, ми можемо передавати аргументи по одному, наприклад, asList (1, 2, 3), або, якщо ми вже маємо масив і хочемо передати його вміст функції, ми використовуємо розповсюдження оператор (префікс масиву *):

val a = arrayOf(1, 2, 3) 
val list = asList(-1, 0, *a, 4)

5

Якщо функція, яка приймає параметр vararg (Змінна кількість аргументів), як:

fun sum(vararg data:Int)
{
   // function body here         
}

Тепер, щоб викликати цей метод, ми можемо зробити:

sum(1,2,3,4,5)

Але що, якщо ми маємо ці значення в масиві, наприклад:

val array= intArrayOf(1,2,3,4,5)

тоді, щоб викликати цей метод, ми повинні використовувати оператор поширення, наприклад:

 sum(*array)

Тут * (оператор поширення) передаватиме весь вміст цього масиву.

* масив еквівалентний 1,2,3,4,5

Але зачекайте хвилинку, а якщо ми називаємо це так: sum(array) це дасть нам помилку часу компіляції Type Mismatch:

Type mismatch.
Required:Int
Found:IntArray

Проблема полягає в тому, що sumфункція приймає vararg Intпараметр (який приймає значення типу: 1,2,3,4,5), і якщо ми передаємо масив, він буде переданий як IntArray.


4

У Java ви можете передавати масив як є, але перевага розпакування масиву за допомогою оператора розповсюдження *полягає в тому, що оператор поширення дозволяє поєднувати значення з масиву та деякі фіксовані значення в одному виклику. Java цього не підтримує.


Проголосував, бо я запитав себе, чому вони це реалізували так. Я все ще не впевнений у цьому на 100%. Я маю на увазі, чи не могли вони просто зробити це в більшості випадків?
Тім Бюте

1
@ TimBüthe У деяких випадках неможливо зробити висновок, розглянемо наступні випадки val resultOne = arrayOf(intArrayOne, intArrayTwo)та val resultTwo = arrayOf(*intArrayOne, *intArrayTwo). Тип resultOneі resultTwoє відповідно Array<Int>і Array<Array<Int>>. Я вважаю, що це одна з причин
Фарід,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.