Я пытаюсь изучить некоторые основы Android и использовать этот пользовательский интерфейс для генерации случайного числа при нажатии:
@Composable
fun DiceWithButton(modifier: Modifier = Modifier) {
var value by remember { mutableStateOf(1) }
fun rollNew() {
value = (1..6).random()
}
Column(modifier = blabla) {
Text(text = value.toString())
Button(onClick = rollNew) {
Text("Roll")
}
}
}
Не удается создать сообщение Type mismatch: inferred type is Unit but () -> Unit was expected
Но когда я переписываю rollNew как лямбда, все работает нормально:
var rollNew = {
value = (1..6).random()
}
Меня это сбивает с толку, поскольку я работаю на Python/Javascript/Golang, где любое значение функции одинаково можно вызвать с помощью ().
В чем разница в Котлине? Почему rollNew имеет тип Unit, если он определен с помощью fun? Или Котлин пытается вызвать rollNew, когда это функция, поэтому Unit?
Ах, значит, в Котлине функции не являются первоклассными гражданами, а лямбды являются ими, и :: используется для преобразования функции в лямбду?
Нет, функции являются первоклассными, и :: не преобразует функцию в лямбду, а вместо этого создает ссылку на эту функцию (чтобы ее можно было передать в качестве параметра). Дополнительную информацию см. в других разделах на связанной странице.
Вдобавок к этому: это потому, что в Котлине упоминание имени функции по умолчанию является признаком того, что вы хотите ее выполнить. (Круглые скобки не всегда необходимы, например, если единственным параметром является лямбда.) Поэтому, чтобы использовать его в качестве ссылки, вам нужен некоторый синтаксис, который представляет собой ведущий ::.





Чтобы использовать функцию, объявленную в объявлении функции, как значение типа функции, вы должны использовать вызываемое ссылочное выражение , т.е.
Button(onClick = ::rollNew)
Простое написание имени функции само по себе является недопустимым синтаксисом.
Меня это сбивает с толку, поскольку я работаю на Python/Javascript/Golang, где любое значение функции одинаково можно вызвать с помощью ().
В Котлине rollNew не является «значением функции». Это имя, объявленное в объявлении функции, а не само по себе выражение. ::rollNew — это значение функции, а также лямбда-выражения и объявления анонимных функций (эти два вместе называются функциональными литералами).
В чем разница в Котлине? Почему
rollNewимеет типUnit, если он определен с помощьюfun? Или Котлин пытается вызватьrollNew, когда это функция, поэтомуUnit?
rollNew сам по себе не является выражением вообще, поэтому говорить о его типе — нонсенс. Как вы догадались, это просто компилятор «догадывается», как вы хотите его вызвать. Помимо сообщения об ошибке несравнимых типов, вы также получаете сообщение об ошибке «Ожидается вызов функции rowNew()».
Ах, значит, в Котлине функции не являются первоклассными гражданами, а лямбды являются ими, и
::используется для преобразования функции в лямбду?
Это объединение синтаксиса и семантики. Лямбда-выражения и вызываемые ссылки являются синтаксическими особенностями языка. Семантически они оба представляют идею «функций» — типом этих выражений, среди прочего, являются типы функций. Возможность передавать «функции» и вызывать их — вот что делает функции первоклассными, независимо от синтаксиса, который вы для этого используете. И, конечно же, :: ничего не преобразует в лямбды. Это полностью отличается от синтаксиса лямбда-выражения.
По аналогии, это похоже на то, что val str = Foo выдает ошибку, но val str = "Foo" компилируется и приходит к выводу, что «строки не являются гражданами первого класса, а строковые литералы».
Тем не менее, обобщенные функции (функции с параметрами свободного типа) действительно не являются первоклассными гражданами, но это уже другая история.
Чтобы ссылаться на функцию Kotlin вместо лямбды, вам нужно использовать
::. В вашем примере это будетonClick = ::rollNew.