Java 实现MapChangeListener的onChanged-为什么会出现这种情况

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表达式的参数-注意,括

在我的JavaFX中,我尝试使用一个
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)