Java 实现MapChangeListener的onChanged-为什么会出现这种情况
在我的JavaFX中,我尝试使用一个Java 实现MapChangeListener的onChanged-为什么会出现这种情况,java,javafx,lambda,listener,Java,Javafx,Lambda,Listener,在我的JavaFX中,我尝试使用一个observeMap和一个MapChangeListener,它侦听键和值的更改(添加/删除键或相应的值),然后执行其工作 为了使侦听器有效,要实现的方法是: void onChanged(MapChangeListener.Change<? extends K,? extends V> change) void onChanged(mapchangelister.Change这两个变量是等效的。第一个变量是定义lambda表达式的参数-注意,括
observeMap
和一个MapChangeListener
,它侦听键和值的更改(添加/删除键或相应的值),然后执行其工作
为了使侦听器有效,要实现的方法是:
void onChanged(MapChangeListener.Change<? extends K,? extends V> change)
void onChanged(mapchangelister.Change这两个变量是等效的。第一个变量是定义lambda表达式的参数-注意,括号中包含了整个Change参数。这允许编译器知道与之匹配的重载
第二个是简单的强制转换。您正在告诉编译器要匹配此lambda的方法签名的类型。(MapChangeListener)
将整个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((整数a)->{})
时,编译器最终会将其转换为Java字节码(在.class文件中),这相当于:
foo.bar(new IntfB() {
public void doSomething(Integer a) {
}
});
这种等价性就是我们所说的
使用lambda最大的可能也是唯一的区别是,它使代码更短,有时使代码更可读,有时使代码更不可读
由于lambda减少了您需要键入的内容的数量,因此当存在如示例中所示的重载方法时,编译器很容易产生对lambda表达式不明确的情况。请记住,编译器需要首先确定哪个重载,然后它将帮助您实例化对象
当您编写foo.bar((双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(invalizationListener)
冲突。有两种解决方法,一种是显式表示类型(第一个代码段),另一种是将编译器定向到正确的重载(第二个),这与lambda语义无关
要重申第二点,假设你有
void print(String s)
及
召唤
print(null)
会导致歧义。解决方案是print((String)null)
这当然不是类型转换,因为null没有类型,而是一个编译器注释。给了你一个额外的例子来说明它们之间的区别。好的,这让我更加理解了。但是我有一个问题。每当我看到这一点,就像在你的例子中一样,我都会希望有a1.doSomething(a2)
在bar
方法的主体中,区分a1
的类型是IntfA
和a2
的类型是Integer
。我是对的吗?@Kenna我不理解你的问题。不可能有a1.doSomething(a2)
不,我的意思是foo.bar((Integer a)->{})
给出了doSomething(Integer a)
的实现,然后调用bar(IntfA a)
。现在我应该期望在bar内我调用了doSomething(Integer a)
。但是由于doSomething(Integer a)
和bar(IntfA a)的参数
有相同的名字a
,我想区分,我说了a1
和a2
。我的意思是我应该在条内调用doSomething
,否则(整数a)->{}
将是无效的,因为它不是在bar
内部调用的。或者我遗漏了什么?太好了!非常感谢Jai!特别是最后一部分是我感兴趣的,你让我更好地理解了。事实上,我在我的评论中的意思是,通常在bar
类方法内部会调用doSomething
-like方法(使用lambda或方法引用或匿名类实现),但如果没有,则不会调用doSomething
方法(而是实现)。@Kenna看到人们
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!");
});
void print(String s)
void print(Integer i)