簡體   English   中英

實現MapChangeListener的onChanged - 為什么這個lambda

[英]Implementing onChanged of MapChangeListener - why this lambda

在我的JavaFX中,我嘗試了一個ObservableMap<String, String>和一個MapChangeListener來監聽鍵和值的變化(添加/刪除鍵或相應的值),然后完成它的工作。

為了使監聽器有效,實現的方法是:

void onChanged(MapChangeListener.Change<? extends K,? extends V> change)

我首先使用lambda表達式做了什么,不會產生任何錯誤:

map.addListener((MapChangeListener.Change<? extends String, ? extends String> change) -> {
    //code here to implement onChange method
}

這是我發現的,仍然不會產生任何錯誤:

map.addListener((MapChangeListener<String, String>) change -> {
    //code here to implement onChange method
}

請注意這兩個不同示例中圓括號的位置。 第二個似乎是我演員,但我真的不明白為什么第二個選項有效。
請問有人能解釋一下嗎?

PS:實際上,我遇到了這個,因為我正在處理一個問題
ObservableMap<String, List<String>>
這是一個多圖,上面兩個的第一個“方式”不起作用(正確的調整)。 /編輯:我用第一個“方式”再次嘗試,實際上它確實有效,代碼上有一個錯誤我沒注意到END EDIT / 然后我嘗試了第二個選項,它確實有效,我很茫然。 然后我用簡單的地圖<String, String>發現了同樣的“行為”,並且出現了這個問題。

這兩個是等價的。 第一個,您正在定義lambda表達式的參數 - 請注意您的括號覆蓋整個更改參數。 這允許編譯器知道哪個重載與之匹配。

第二個只是一個演員。 您告訴編譯器哪種方法簽名與此lambda匹配。 (MapChangeListener<String, String>)將整個lambda表達式轉換為MapChangeListener ,因此編譯器知道它確實是addListener(MapChangeListener) 由於您已經定義了MapChangeListener定義的單個參數,因此編譯器也不會抱怨它也是錯誤的。

編輯

現在我有更多的時間,我會給你一些具體的例子,這將有助於你更深入地了解。

public class Foo {
    public final void bar(IntfA a) {}
    public final void bar(IntfB b) {}
    public final void bar(IntfC c) {}
}

@FunctionalInterface
public interface IntfA {
    void doSomething(Double a);
}

@FunctionalInterface
public interface IntfB {
    void doSomething(Integer a);
}

@FunctionalInterface
public interface IntfC {
    void doSomething(Double a);
}

public class Test {
    public static void main(String[] args)
    {
        Foo foo = new Foo();

        foo.bar(a -> {}); // Ambiguous
        foo.bar((Integer a) -> {}); // Okay, this is IntfB
        foo.bar((Double a) -> {}); // Ambiguous between IntfA and IntfC
        foo.bar((IntfC) a -> {}); // No longer ambiguous since you specified that it's IntfC
        foo.bar((IntfC) (a, b) -> {}); // Method signature does not match IntfC
    }
}

編輯2

看來你需要一點幫助。

當定義的方法bar(IntfA)你期望的目的IntfA ,無論是否IntfA是一個接口類型或類的類型。

然后,lambda表達式只是編譯時方便的語法。 當我編寫foo.bar((Integer a) -> {}) ,編譯器最終會將其轉換為Java字節碼(在.class文件中),它等效於:

foo.bar(new IntfB() {
    public void doSomething(Integer a) {
    }
});

這種等價就是我們所謂的匿名類

使用lambda的最大和可能唯一的區別是,它使您的代碼更短。 有時它會使您的代碼更具可讀性,有時它會降低您的代碼的可讀性。

由於lambda減少了你需要輸出的東西的數量,因此當存在像示例中的重載方法時,很容易有一個lambda表達式對於編譯器是不明確的。 請記住,編譯器需要首先確定哪個重載,然后它將幫助您為您實例化對象。

當你編寫foo.bar((Double a) -> {}) ,編譯會注意到你有一個lambda表達式,它接受一個Double參數並且什么都不返回。 然后它將查看bar()的三個重載。 它注意到bar(IntfA)bar(IntfC)接受了一個功能接口,並且兩個接口的方法都接受一個Double參數並且不返回任何內容。 此時,編譯器不確定是否應該生成相當於兩組代碼的字節碼:

選擇1:

foo.bar(new IntfA() {
    public void doSomething(Double a) {
    }
});

選擇2:

foo.bar(new IntfC() {
    public void doSomething(Double a) {
    }
});

如果你寫foo.bar((IntfC) a -> {}) ,你已經暗示你希望它匹配foo.bar(IntfC)重載的編譯器。 編譯器看到你有一個未知類型的參數,但由於你已經告訴它與IntfC匹配,它將假設參數是Double

現在到最后一部分,調用foo.bar(IntfA)不會自動調用IntfA指定的doSomething(Double a)方法。 在我的例子中, bar()方法什么也沒做,但通常人們會寫一些有用的東西。

示例:

public final void bar(IntfB obj) {
    if (obj == null)
        System.out.println("I was waiting for an IntfB object but I got nothing!");
    else
        obj.doSomething(100);
}

foo.bar((Integer a) -> {
    System.out.println("I got " + a + " marks for my exam!");
});

這導致“我的考試得到了100分!” 要在控制台上打印。

Lambda實際上不需要表達其類型,除非存在歧義。

如果不輸入change ,則會與具有相同參數長度的addListener(InvalidationListener)沖突。 有兩種方法可以通過顯式表達類型(您的第一個片段)或通過將編譯器指向正確的重載(第二個)來解決這個問題,這與lambda語義無關。

重申第二點,說你有

void print(String s)

void print(Integer i)

調用

print(null)會導致歧義。 解決方案是print((String)null) ,當然不是類型轉換,因為null沒有類型,而是編譯器注釋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM