首页 文章

功能定义:fun vs val

提问于
浏览
31

我很好奇在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 回答

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

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

    • fun f 只是一种常用的方法,而 val g 实际上是一个返回另一个函数的高阶函数

    • val g 涉及创建一个新类,如果你的目标是Android,那就不好了

    • val g 需要不必要的装箱和拆箱
      无法从java轻松调用

    • val g :Kotlin中的 A().g(42) 与Java中的 new A().getG().invoke(42)

    UPDATE:

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

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

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

  • 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中 .

相关问题