函数定义:fun vs val
我很好奇在Kotlin中定义成员函数的建议方式是什么。 考虑这两个成员函数:
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)
即我必须首先部分应用它。
因为在使用val
函数的时候,我不必经历这个麻烦,所以我有理由用fun
定义非单元成员函数吗? 我缺少性能或语义上的差异吗?
Kotlin完全是关于Java的互操作性,并且定义一个函数作为一个val
将在互操作性方面产生一个完全不同的结果。 以下Kotlin类:
class A { fun f(x: Int) = 42 val g = fun(x: Int) = 42 }
实际上相当于:
public class A { private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() { @Override public Integer invoke(final Integer integer) { return 42; } }; public int f(final int value) { return 42; } public Function1<Integer, Integer> getG() { return gref; } }
正如你所看到的,主要的区别是:
-
fun f
只是一个常用的方法,而val g
实际上是一个返回另一个函数的高阶函数 -
val g
涉及创建一个新的类,如果你的目标是Android,那就不好了 -
val g
需要不必要的装箱和拆箱 -
val g
不能轻易地从java:A().g(42)
in Kotlin vsnew A().getG().invoke(42)
in Java
更新:
关于A::f
语法。 编译器会为每 A::f
发生一个额外的Function2<A, Integer, Integer>
类,所以下面的代码会产生两个额外的类,每个类有7个方法 :
val first = A::f val second = A::f
Kotlin编译器目前还不够聪明,以优化这种类型的东西。 你可以在这里为这个问题投票https://youtrack.jetbrains.com/issue/KT-9831 。 如果你有兴趣,这里是每个类在字节码中的外观: https : //gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3
下面是一些代码,说明如何使用f和g:
fun main(args: Array<String>) { val a = A() exe(ag) // OK //exe(af) // does not compile exe { af(it) } // OK } fun exe(p: (Int) -> Int) { println(p(0)) }
你可以看到g是一个可以像lambda一样使用的对象,但是f不能。 要同样使用f,你必须用lambda包装它。