首页 文章

命名函数vs lambda反射

提问于
浏览
0

我仍然在学习Kotlin,并试图了解其核心原则 . 我没有得到的是:

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 )?是否有任何语言设计原则需要功能签名的这种差异?

还有一个额外的问题 - 为什么我们需要使用运算符 :: . 没有它就无法编译 . 但为什么语言设计需要这个?不会 val y = x 工作得很好而且更简单吗?

3 回答

  • 1

    fun x() 是来自过程编程的普通命名函数 . val y 是一个包含对 x 的引用的属性 . val z 是函数式编程的匿名函数 .

    :: 是'function reference',是程序编程和函数编程之间的一种桥梁 .

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

  • 0

    IF 你从我的角度思考,我认为你可以立即理解它 .

    • x 只是标识符而不是变量,因此您无法直接引用它 .

    • fun x() 是一个派生自 Function0 的类 .

    • 表达式 ::xfun x() 类型的实例,在kotlin中称为function reference expression .

    Kotlin在语言中使功能和属性成为一流公民,并对其进行反思 .

    有时函数在kotlin中有自己的类,就像java-8 lambda表达式一样 . 但function reference expression不能与diff receiver一起使用 .

    当调用inline function后跟一个lambda时,lambda和函数都将在调用站点内联 . 但是使用非内联function reference expression进行呼叫,只有inline function将在呼叫站点内联 .

  • 0

    我想更好地理解字节码

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

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

相关问题