[英]Is it possible in Java to compare references to instances of class if equals() method is overridden?
[英]Java 8 method references and overridden methods
我一直在 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);
兩個流管道做同樣的事情,它們打印出三個數字,每行一個。 區別在於他們的第二行,似乎可以簡單地替換繼承層次結構中的類名,只要它有方法(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);
}
}
在更改第一個、第二個和第三個分配以使用這些類之后,我可以替換方法引用 (CustomCustomHashSet::stream),並且毫不奇怪它們在所有情況下都打印出調試消息,即使我使用 Collection::stream 時也是如此。 似乎您無法使用方法引用調用超級、覆蓋的方法。
有什么運行時差異嗎? 什么是更好的做法,參考頂級接口/類或使用具體的已知類型(Set)? 謝謝!
編輯:為了清楚起見,我知道繼承和 LSP,我的困惑與 Java 8 中方法引用的設計有關。我的第一個想法是更改方法引用中的類會改變行為,它會調用來自所選類的 super 方法,但正如測試所示,它沒有區別。 更改創建的實例類型確實會更改行為。
甚至方法引用也必須遵守方法覆蓋的 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));
}
您不必更改方法主體。
一個Set
是一個Collection
。 Collection
有一個stream()
方法,所以Set
也有同樣的方法,就像所有Set
實現(例如HashSet
、 TreeSet
等)一樣。
將方法標識為屬於任何特定的超類型沒有區別,因為它始終會解析為運行時對象實現聲明的實際方法。
參見Liskov 替換原則:
如果 S 是 T 的子類型,則類型 T 的對象可以替換為類型 S 的對象,而不會改變該程序的任何所需屬性
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.