功能定义:fun vs val

Mat*_*ias 32 kotlin

我很好奇在Kotlin中定义成员函数的建议方法是什么.考虑这两个成员函数:

class A {

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42

}
Run Code Online (Sandbox Code Playgroud)

这些似乎完成了同样的事情,但我发现了微妙的差异.

val基础的定义,举例来说,似乎是在某些情况下更加灵活.也就是说,我无法通过直接的方式f与其他功能合作,但我可以g.为了玩弄这些定义,我使用了funKTionale库.我发现这不编译:

val z = g andThen A::f // f is a member function
Run Code Online (Sandbox Code Playgroud)

但如果f被定义为val指向同一个函数,它就会编译得很好.为了弄清楚发生了什么事情,我问的IntelliJ明确定义的类型::fg对我来说,和它给了我这样的:

val fref: KFunction1<Int, Int> = ::f

val gref: (Int) -> Int = g
Run Code Online (Sandbox Code Playgroud)

所以一个是类型KFunction1<Int, Int>,另一个是类型(Int) -> Int.很容易看出它们都代表了类型的功能Int -> Int.

这两种类型有什么区别,哪些情况重要?我注意到对于顶级函数,我可以使用任何一个定义来编写它们,但是为了使前面的组合编译,我必须像这样编写它:

val z = g andThen A::f.partially1(this)
Run Code Online (Sandbox Code Playgroud)

即我必须首先部分应用它this.

因为在使用vals作为函数时我不必经历这种麻烦,我有没有理由使用fun?来定义非单元成员函数?我缺少的性能或语义是否存在差异?

Vla*_*nov 34

Kotlin完全关注Java互操作性,并将功能定义val为在互操作性方面产生完全不同的结果.以下Kotlin类:

class A {
  fun f(x: Int) = 42
  val g = fun(x: Int) = 42
}
Run Code Online (Sandbox Code Playgroud)

实际上相当于:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,主要区别在于:

  1. fun f这只是一种常用的方法,而val g实际上是一个返回另一个函数的高阶函数
  2. val g 涉及创建一个新类,如果你的目标是Android,那就不好了
  3. val g 需要不必要的装箱和拆箱
  4. val g不能从java中轻松调用:A().g(42)在Kotlin和new A().getG().invoke(42)Java中

更新:

关于A::f语法.编译器将为每次出现生成一个额外的Function2<A, Integer, Integer>类,因此以下代码会产生两个额外的类,每个类有7个方法: A::f

val first = A::f
val second = A::f
Run Code Online (Sandbox Code Playgroud)

Kotlin编译器目前还不够聪明,无法优化此类事物.您可以在此处投票支持此问题https://youtrack.jetbrains.com/issue/KT-9831.如果您感兴趣,以下是每个类在字节码中的显示方式:https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3

  • 太好了,这正是我想要的信息。跟进问题:使用`:: f`“调整”`f`是否会导致Kotlin创建内部函数类,类似于您在此描述的对g所做的事情? (2认同)

Dou*_*son 3

下面的一些代码显示了 f 和 g 在使用方面的不同之处:

fun main(args: Array<String>) {
    val a = A()
    exe(a.g)  // OK
    //exe(a.f)  // does not compile
    exe { a.f(it) }  // OK
}

fun exe(p: (Int) -> Int) {
    println(p(0))
}
Run Code Online (Sandbox Code Playgroud)

地点fg地点:

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42
Run Code Online (Sandbox Code Playgroud)

您可以看到 g 是一个可以像 lambda 一样使用的对象,但 f 不能。要类似地使用 f,您必须将其包装在 lambda 中。

  • 这不只是因为语法不同吗?`ag` 是对 fun 的引用;`af` 不是;您应该使用方法引用语法:`a::f`。刚刚测试过,“exe(a::f)”确实有效! (2认同)