我已经在Java 8中使用lambdas和方法引用了一段时间,有一件事我不明白。以下是示例代码:
var cbCollection = this.state.cbData.map(function(elem, index) {
//this.setCheckboxValue(elem.IsSelected);
var x=<span><label><input type = "checkbox" name = "cbCodes" id = {'cb-'+index} value={elem.Id} checked={elem.IsSelected} disabled={false} onChange={this.UpdateCheckbox.bind(this,elem)}/>{elem.DiagCodes}</label><br /></span>
return x;
}.bind(this));
两个流管道做同样的事情,他们打印出三个数字,每行一个。不同之处在于它们的第二行,似乎只要它具有方法就可以简单地替换继承层次结构中的类名(Collection接口具有默认方法“stream”,而未在Set接口中重新定义)。 我尝试了如果使用这些类一次又一次地重新定义方法会发生什么:
Set<Integer> first = Collections.singleton(1);
Set<Integer> second = Collections.singleton(2);
Set<Integer> third = Collections.singleton(3);
Stream.of(first, second, third)
.flatMap(Collection::stream)
.map(String::valueOf)
.forEach(System.out::println);
Stream.of(first, second, third)
.flatMap(Set::stream)
.map(String::valueOf)
.forEach(System.out::println);
在更改第一,第二和第三个赋值以使用这些类之后,我可以替换方法引用(CustomCustomHashSet :: stream)并且毫不奇怪它们在所有情况下都打印出调试消息,即使我使用Collection :: stream 。看来你不能用方法引用来调用super,overriden方法。
是否存在运行时差异?什么是更好的做法,请参考顶级接口/类或使用具体的已知类型(Set)? 谢谢!
编辑: 为了清楚,我知道继承和LSP,我的困惑与Java 8中方法引用的设计有关。我的第一个想法是在方法引用中更改类会改变行为,它会调用超级来自所选类的方法,但正如测试所示,它没有任何区别。更改创建的实例类型确实会改变行为。
答案 0 :(得分:3)
Merchant: <strong>mFortune</strong> | CPA Commission: <strong> £2270</strong> | Downloads: <strong>34</strong> | Net Revenue: <strong>£-161.4</strong><br>Merchant: <strong>PocketWin</strong> | CPA Commission: <strong> £1330</strong> | Downloads: <strong>22</strong> | Net Revenue: <strong>£-134.77</strong><br>Merchant: <strong>Mr Spin</strong> | CPA Commission: <strong> £680</strong> | Downloads: <strong>31</strong> | Net Revenue: <strong>£13.22</strong><br>
是 Set
。 Collection
有一个Collection
方法,因此stream()
也有同样的方法,所有Set
实施都有(例如Set
,HashSet
等)。
将方法标识为属于任何特定超类型没有区别,因为它总是会解析为运行时对象的实现声明的实际方法。
请参阅Liskov Substitution Principle:
如果S是T的子类型,那么类型T的对象可以用类型S的对象替换而不改变该程序的任何所需属性
答案 1 :(得分:2)
甚至方法引用也必须遵守方法覆盖的OOP原则。否则,代码如
public static List<String> stringify(List<?> o) {
return o.stream().map(Object::toString).collect(Collectors.toList());
}
关于用于方法引用的类名:我更喜欢使用声明该方法的最通用的类或接口。
原因是:您编写了处理Set
集合的方法。稍后您会发现您的方法对Collection
的集合也很有用,因此您可以相应地更改方法签名。现在,如果方法中的代码始终引用Set方法,则必须将这些方法引用调整为。
这
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Set::stream).forEach(e -> System.out.println(e));
}
到
public static <T> void test(Collection<Collection<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
您也需要更改方法体,而如果您已将方法编写为
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
您无需更改方法体。