功能定义:fun vs val

时间:2016-03-20 19:09:31

标签: kotlin

我很好奇在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为我明确定义::fg的类型,它给了我这个:

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函数时我不必经历这个麻烦,我有理由使用fun来定义非单元成员函数吗?我缺少的性能或语义是否存在差异?

2 个答案:

答案 0 :(得分:33)

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;
  }
}

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

  1. fun f只是一种常用的方法,而val g实际上是一个返回另一个函数的高阶函数
  2. val g涉及创建一个新类,如果您定位Android
  3. 则该类不好
  4. val g需要不必要的装箱和拆箱
  5. val g无法从java中轻松调用:Kotlin中的A().g(42)与Java中的new A().getG().invoke(42)
  6. <强>更新

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

    val first = A::f
    val second = A::f
    

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

答案 1 :(得分:1)

这里有一些代码显示了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))
}

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