在我的JavaFX中,我尝试使用var img = document.getElementById('img');
var height = window.getComputedStyle(img,null).getPropertyValue("height");
var width= window.getComputedStyle(img,null).getPropertyValue("width");
alert(height);
alert(width);
和ObservableMap<String, String>
来监听键和值的更改(添加/删除键或相应的值),然后完成其工作。
为了使监听器有效,实现的方法是:
MapChangeListener
我首先使用lambda表达式做了什么,不会产生任何错误:
void onChanged(MapChangeListener.Change<? extends K,? extends V> change)
以下是我发现的,仍然没有产生任何错误:
map.addListener((MapChangeListener.Change<? extends String, ? extends String> change) -> {
//code here to implement onChange method
}
注意这两个不同例子中圆括号的位置。第二个似乎是我演员,但我真的不明白为什么第二个选项有效 有人可以解释一下吗?
P.S。:实际上,我遇到了这个因为我正在处理一个问题
map.addListener((MapChangeListener<String, String>) change -> {
//code here to implement onChange method
}
,
这是一个多图,上面两个的第一个“方式”不起作用(正确的调整)。 /编辑:我用第一个“方式”再次尝试,实际上它确实有效,我没注意到的代码出现了错误 END EDIT / 。然后我尝试了第二个选项,它确实有效,我很茫然。然后我用简单的地图ObservableMap<String, List<String>>
发现了同样的“行为”,并且出现了这个问题。
答案 0 :(得分:2)
这两个是等价的。第一个,您正在定义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) {
}
});
我们称之为Anonymous Class。
使用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)
并不会自动调用doSomething(Double a)
指定的IntfA
方法。在我的例子中,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!");
});
这导致&#34;我的考试得到了100分!&#34;在控制台上打印。
答案 1 :(得分:1)
实际上,Lambda不需要表达其类型,除非存在歧义。
如果您不输入change
,它将与具有相同参数长度的addListener(InvalidationListener)
冲突。有两种方法可以通过显式表达类型(您的第一个片段)或通过将编译器指向正确的重载(第二个)来解决这个问题,这与lambda语义无关。
重申第二点,说你有
void print(String s)
和
void print(Integer i)
致电
print(null)
会导致含糊不清。解决方案是print((String)null)
,当然不是类型转换,因为null没有类型,而是编译器注释。