Мне интересно, что такое предложенный способ определения функций-членов в Котлине. Рассмотрим эти две функции-члена:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
Они, похоже, совершают одно и то же, но я обнаружил тонкие различия.
Определение на основе val
, например, представляется более гибким в некоторых сценариях. То есть, я не мог выработать прямой способ составить f
с другими функциями, но я мог бы с g
. Чтобы играть с этими определениями, я использовал библиотеку funKTionale. Я обнаружил, что это не скомпилируется:
val z = g andThen A::f // f is a member function
Но если f
были определены как val
, указывающие на одну и ту же функцию, он будет компилироваться просто отлично. Чтобы выяснить, что происходит, я попросил IntelliJ явно определить тип ::f
и g
для меня, и он дает мне следующее:
val fref: KFunction1<Int, Int> = ::f
val gref: (Int) -> Int = g
Итак, один имеет тип KFunction1<Int, Int>
, другой - типа (Int) -> Int
. Легко видеть, что обе представляют собой функции типа Int -> Int
.
В чем разница между этими двумя типами, и в каких случаях это имеет значение? Я заметил, что для функций верхнего уровня я могу скомпоновать их с помощью любого определения, но для того, чтобы сделать вышеупомянутый состав компиляцией, мне пришлось написать его так:
val z = g andThen A::f.partially1(this)
то есть. Я должен был сначала применить его к this
.
Так как мне не нужно проходить эту проблему при использовании val
для функций, есть ли причина, почему я должен когда-либо определять функции-члены не-Unit с помощью fun
? Есть ли разница в производительности или семантике, которые мне не хватает?