Java 8方法引用和重写方法

Elo*_*ryx 9 java java-8 method-reference

我已经在Java 8中使用lambdas和方法引用了一段时间,有一件事我不明白.这是示例代码:

    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);
Run Code Online (Sandbox Code Playgroud)

两个流管道做同样的事情,他们打印出三个数字,每行一个.不同之处在于它们的第二行,似乎只要它具有方法就可以简单地替换继承层次结构中的类名(Collection接口具有默认方法"stream",而未在Set接口中重新定义).我尝试了如果使用这些类一次又一次地重新定义方法会发生什么:

private static class CustomHashSet<E> extends HashSet<E> {
    @Override
    public Stream<E> stream() {
        System.out.println("Changed method!");
        return StreamSupport.stream(spliterator(), false);
    }
}

private static class CustomCustomHashSet<E> extends CustomHashSet<E> {
    @Override
    public Stream<E> stream() {
        System.out.println("Changed method again!");
        return StreamSupport.stream(spliterator(), false);
    }
}
Run Code Online (Sandbox Code Playgroud)

在更改第一,第二和第三个赋值以使用这些类之后,我可以替换方法引用(CustomCustomHashSet :: stream),并且毫不奇怪它们在所有情况下都打印出调试消息,即使我使用Collection :: stream也是如此.看来你不能用方法引用调用super,overriden方法.

运行时间有差异吗?什么是更好的做法,请参考顶级接口/类或使用具体的已知类型(Set)?谢谢!

编辑:为了清楚,我知道继承和LSP,我的困惑与Java 8中方法引用的设计有关.我首先想到的是,在方法引用中更改类会改变行为,它会调用它来自所选类的超级方法,但正如测试所示,它没有任何区别.更改创建的实例类型确实会改变行为.

Tho*_*ger 5

甚至方法引用也必须遵守方法覆盖的 OOP 原则。否则,代码如下

public static List<String> stringify(List<?> o) {
    return o.stream().map(Object::toString).collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)

不会按预期工作。

至于用于方法引用的类名:我更喜欢使用声明方法的最通用的类​​或接口。

原因是:您编写方法来处理Set. 稍后您会发现您的方法可能对 的集合也很有用Collection,因此您可以相应地更改您的方法签名。现在,如果方法中的代码始终引用 Set 方法,则您也必须调整这些方法引用:

public static <T> void test(Collection<Set<T>> data) {
    data.stream().flatMap(Set::stream).forEach(e -> System.out.println(e));
}
Run Code Online (Sandbox Code Playgroud)

public static <T> void test(Collection<Collection<T>> data) {
    data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
Run Code Online (Sandbox Code Playgroud)

您也需要更改方法主体,而如果您将方法编写为

public static <T> void test(Collection<Set<T>> data) {
    data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
Run Code Online (Sandbox Code Playgroud)

您不必更改方法主体。