命名函数vs lambda反射

我仍然在学习科特林,并试图理解其核心原则。 我没有得到的是这样的:

fun x() : Int { return 10 } val y : () -> Int = ::x val z : () -> Int = { 10 } fun main(args: Array<String>) { println(::x) println(y) println(z) } 

我们得到以下输出:

 fun x(): kotlin.Int fun x(): kotlin.Int () -> kotlin.Int 

我的问题是为什么输出不一样(我相信这些功能应该是可以互换的,等价的)? 我认为所有函数的类型应该是() -> Int 。 为什么我们保留原来的名字和函数签名( fun x ),尽管它被分配了不同的名字( y )? 有什么语言设计原则需要在功能签名方面有所不同?

还有一个额外的问题 – 为什么我们需要使用operator :: 。 没有它就不能编译。 但为什么这是语言设计所要求的呢? val y = x会不会很好,而且简单得多?

fun x()是程序编程中一个普通的命名函数。 val y是持有对x的引用的属性。 val z是函数式编程的匿名函数。

::是一个“函数引用”,是程序和函数编程之间的一种桥梁。

默认情况下,你的功能应该很fun 。 另一方面,Lambda表达式(匿名函数)旨在作为回调传递给其他函数。

如果您从我的角度思考,我认为您可以立即理解。

  • x只是一个标识符而不是一个变量,所以你不能直接引用它。
  • fun x()是从Function0派生的类。
  • 表达式::xfun x()的类型的实例,这在kotlin中被称为函数引用表达式 。

科特林在功能和性能方面使语言中的一流公民,并对其进行反思。

有时候函数在java-8 lambda表达式中拥有kotlin自己的类。 但函数引用表达式不能与diff 接收器一起使用 。

当调用一个内联函数后跟一个lambda时,lambda&函数将被内联在call-site上。 但使用非内联函数引用表达式调用时,只有内联函数将在内联函数中被内联。

我认为更好的理解看字节码

  private final static Lkotlin/jvm/functions/Function0; y @Lorg/jetbrains/annotations/NotNull;() // invisible // access flags 0x1A // signature Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>; // declaration: kotlin.jvm.functions.Function0<java.lang.Integer> private final static Lkotlin/jvm/functions/Function0; z @Lorg/jetbrains/annotations/NotNull;() // invisible // access flags 0x19 public final static main([Ljava/lang/String;)V @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 L0 ALOAD 0 LDC "args" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V L1 LINENUMBER 6 L1 GETSTATIC MainKt$main$1.INSTANCE : LMainKt$main$1; ASTORE 1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L2 LINENUMBER 7 L2 GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0; ASTORE 1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L3 LINENUMBER 8 L3 GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0; ASTORE 1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L4 LINENUMBER 9 L4 RETURN L5 LOCALVARIABLE args [Ljava/lang/String; L0 L5 0 MAXSTACK = 2 MAXLOCALS = 2 // access flags 0x19 public final static x()I L0 LINENUMBER 11 L0 BIPUSH 10 IRETURN MAXSTACK = 1 MAXLOCALS = 0 // access flags 0x19 // signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>; // declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getY() public final static getY()Lkotlin/jvm/functions/Function0; @Lorg/jetbrains/annotations/NotNull;() // invisible L0 LINENUMBER 12 L0 GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0; ARETURN MAXSTACK = 1 MAXLOCALS = 0 // access flags 0x19 // signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>; // declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getZ() public final static getZ()Lkotlin/jvm/functions/Function0; @Lorg/jetbrains/annotations/NotNull;() // invisible L0 LINENUMBER 13 L0 GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0; ARETURN MAXSTACK = 1 MAXLOCALS = 0 // access flags 0x8 static <clinit>()V L0 LINENUMBER 12 L0 GETSTATIC MainKt$y$1.INSTANCE : LMainKt$y$1; CHECKCAST kotlin/jvm/functions/Function0 PUTSTATIC MainKt.y : Lkotlin/jvm/functions/Function0; L1 LINENUMBER 13 L1 GETSTATIC MainKt$z$1.INSTANCE : LMainKt$z$1; CHECKCAST kotlin/jvm/functions/Function0 PUTSTATIC MainKt.z : Lkotlin/jvm/functions/Function0; RETURN MAXSTACK = 1 MAXLOCALS = 0 } 

y和z值的类型为Function0

 static <clinit>()V // here we have static initialization block where y and z are initialized 

x有类型MainKt $ main $ 1

在每种情况下(println),我们只显示方法声明(可见性修饰符,返回类型,签名)而不用调用。 y和z是字节码中的高阶函数,由Function0类表示,x值只是静态函数。

而当你调用println(::x) println(y) println(z)你只需要打印函数声明。 这是可以的,x和y函数的声明与z函数中的不同。 因为y引用了x函数,而z仍然是返回10的高位函数。简单地说y == x,因为你从x赋值给y函数声明。 只是为了注意。 在Java中,我可以用这种方式显示方法声明:

  public static void main(String[] args) { try { System.out.println(Main.class.getMethod("A", null)); // prints public static void Main.A() } catch (NoSuchMethodException e) { e.printStackTrace(); } } public static void A() { System.out.println(15); } 

结论:在你的代码中,你只需要打印函数返回类型,可见性修饰符和签名。