2017-06-14 30 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); 
    } 

結論: 在你的代碼只是打印函數的返回類型,可見改性劑和簽名。