简体   繁体   English

功能定义:fun vs val

[英]Function definition: fun vs val

I'm curious about what is the suggested way to define member functions in Kotlin. 我很好奇在Kotlin中定义成员函数的建议方法是什么。 Consider these two member functions: 考虑这两个成员函数:

class A {

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42

}

These appear to accomplish the same thing, but I found subtle differences. 这些似乎完成了同样的事情,但我发现了微妙的差异。

The val based definition, for instance, seems to be more flexible in some scenarios. 例如,基于val的定义在某些情况下似乎更灵活。 That is, I could not work out a straight forward way to compose f with other functions, but I could with g . 也就是说,我无法通过直接的方式将f与其他函数组合起来,但我可以用g To toy around with these definitions, I used the funKTionale library. 为了玩弄这些定义,我使用了funKTionale库。 I found that this does not compile: 我发现这不编译:

val z = g andThen A::f // f is a member function

But if f were defined as a val pointing to the same function, it would compile just fine. 但是如果f被定义为指向同一函数的val ,那么编译就好了。 To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this: 为了弄清楚发生了什么,我要求IntelliJ为我明确定义::fg的类型,它给了我:

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

val gref: (Int) -> Int = g

So one is of type KFunction1<Int, Int> , the other is of type (Int) -> Int . 所以一个是KFunction1<Int, Int>类型,另一个是类型(Int) -> Int It's easy to see that both represent functions of type Int -> Int . 很容易看出它们都代表Int -> Int类型的函数。

What is the difference between these two types, and in which cases does it matter? 这两种类型有什么区别,哪些情况重要? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so: 我注意到对于顶级函数,我可以使用任何一个定义来编写它们,但是为了使前面的组合编译,我必须像这样编写它:

val z = g andThen A::f.partially1(this)

ie I had to partially apply it to this first. 即我必须首先将其部分应用于this

Since I don't have to go through this hassle when using val s for functions, is there a reason why I should ever define non-Unit member functions using fun ? 因为在使用val s作为函数时我不必经历这种麻烦,所以我有理由使用fun定义非Unit成员函数吗? Is there a difference in performance or semantics that I am missing? 我缺少的性能或语义是否存在差异?

Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. Kotlin完全关注Java互操作性,将函数定义为val将在互操作性方面产生完全不同的结果。 The following Kotlin class: 以下Kotlin类:

class A {
  fun f(x: Int) = 42
  val g = fun(x: Int) = 42
}

is effectively equivalent to: 实际上相当于:

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

As you can see, the main differences are: 如您所见,主要区别在于:

  1. fun f is just a usual method, while val g in fact is a higher-order function that returns another function fun f只是一种常用方法,而val g实际上是一个返回另一个函数的高阶函数
  2. val g involves creation of a new class which isn't good if you are targeting Android val g涉及创建一个新类,如果你的目标是Android,那就不好了
  3. val g requires unnecessary boxing and unboxing val g需要不必要的装箱和拆箱
  4. val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java val g不能从java中轻松调用:Kotlin中的A().g(42) vs Java中的new A().getG().invoke(42)

UPDATE: 更新:

Regarding the A::f syntax. 关于A::f语法。 The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each: 编译器将为每个 A::f出现一个额外的Function2<A, Integer, Integer>类,因此以下代码会产生两个额外的类,每个类有7个方法

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

Kotlin compiler isn't smart enough at the moment to optimize such kind of things. Kotlin编译器目前还不够聪明,无法优化此类事物。 You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831 . 您可以在此处投票支持此问题https://youtrack.jetbrains.com/issue/KT-9831 In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3 如果您感兴趣,以下是每个类在字节码中的显示方式: https//gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3

Here's some code showing how f and g are different when it comes to usage: 这里有一些代码显示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))
}

Where f and g are: 其中fg是:

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42

You can see that g is an object that can be used like a lambda, but f cannot. 你可以看到g是一个可以像lambda一样使用的对象,但f不能。 To use f similarly, you have to wrap it in a lambda. 要类似地使用f,你必须将它包装在lambda中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM