Java 8方法引用和重写方法

时间:2016-09-17 10:50:34

标签: java java-8 method-reference

我已经在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中方法引用的设计有关。我的第一个想法是在方法引用中更改类会改变行为,它会调用超级来自所选类的方法,但正如测试所示,它没有任何区别。更改创建的实例类型确实会改变行为。

2 个答案:

答案 0 :(得分:3)

Merchant: <strong>mFortune</strong> | CPA Commission: <strong> &pound;2270</strong> | Downloads: <strong>34</strong> | Net Revenue: <strong>&pound;-161.4</strong><br>Merchant: <strong>PocketWin</strong> | CPA Commission: <strong> &pound;1330</strong> | Downloads: <strong>22</strong> | Net Revenue: <strong>&pound;-134.77</strong><br>Merchant: <strong>Mr Spin</strong> | CPA Commission: <strong> &pound;680</strong> | Downloads: <strong>31</strong> | Net Revenue: <strong>&pound;13.22</strong><br> SetCollection有一个Collection方法,因此stream()也有同样的方法,所有Set实施都有(例如SetHashSet等)。

将方法标识为属于任何特定超类型没有区别,因为它总是会解析为运行时对象的实现声明的实际方法。

请参阅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));
}

您无需更改方法体。