![](/img/trans.png)
[英]Why is onChanged not called when value changed in LiveData List?
[英]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
}
}
看來你需要一點幫助。
當定義的方法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.