2017-06-14 32 views
0

我仍在学习Kotlin并试图理解其核心原则。我不明白的是这样的:命名函数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)?是否有任何语言设计原则会要求功能签名有所不同?

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

回答

1

fun x()是一个来自程序编程的普通命名函数。 val y是持有对x的参考的财产。 val z是函数式编程的匿名函数。

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

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

+0

是的......我理解你说的话。但是,如果功能是Kotlin中的“头等公民”,为什么我们需要区分功能的功能和程序起源?他们有不同的属性吗?我想不是 - 你可以给他们打电话,你可以分配一些东西,你可以把他们作为参数传递给他人。你也可以对这两种类型命名的函数和lambda表达式做同样的事情。那么为什么分化呢? –

+0

@ V.K。每种语言的每个特征都是一种妥协。匿名函数是对象,它们可以作为参数传递,而在JVM上它们是匿名类。它们是实现函数接口的Any(实际上是'java.lang.Object')的子类,它们有'hashCode','equals','toString'(甚至更多)方法,它们以某种方式影响GC。另一方面,命名函数是JVM上的普通方法。编译为JS时,技术上的差异较小,因为JS中的所有函数都是匿名的。不一致的函数语法是性能和Java互操作性的代价。 –

0

IF您从我的角度思考我认为您可以立即理解它。

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

Kotlin在语言中使功能和属性成为头等公民,并对它们进行反思。

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

当调用inline function后跟一个lambda时,这两个lambda &函数将在内部呼叫站点内联。但使用非内联function reference expression进行调用,则只有inline function将在呼叫站点内联。

+0

'fun x()'不是一个类,它是JVM上的一个方法。 ':: x'是一个匿名类。 –

+0

@ Miha_x64先生,我说它发生在有时并不总是。 –

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

结论: 在你的代码只是打印函数的返回类型,可见改性剂和签名。