Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
701 views
in Technique[技术] by (71.8m points)

kotlin - Function definition: fun vs val

I'm curious about what is the suggested way to define member functions in 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. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. 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. 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:

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. It's easy to see that both represent functions of type 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)

i.e. I had to partially apply it to this first.

Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:

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
  2. val g involves creation of a new class which isn't good if you are targeting Android
  3. val g requires unnecessary boxing and unboxing
  4. val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java

UPDATE:

Regarding the A::f syntax. 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:

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

Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here 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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...