Мне интересно, что такое предложенный способ определения функций-членов в Котлине. Рассмотрим эти две функции-члена:
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? Есть ли разница в производительности или семантике, которые мне не хватает?