由 JS 轉 Java,寫慣了 React,習慣了函式式,因此轉 Java 時也是先學函式式,
語法糖「Syntactic Sugar」
起初,Java 的函式式看起來是匿名類的一個語法糖,
Stream.of(1, 2, 3).filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0;
}
}).collect(Collectors.toList());
Stream.of(1, 2, 3).filter(x -> x % 2 == 0).collect(Collectors.toList());
后來,看到也能傳方法參考時,我陷入了懷疑,
// 類名::static 方法
Stream.of(4, 2, 3).sorted(Integer::compareTo);
// 物件::物件方法
Stream.of(1, 2, 3).forEach(System.out::println);
// 類名::物件方法
Stream.of(4, 2, 3).sorted(Integer::compareTo);
不過再想想,還是可以理解成語法糖:
Stream.of(1, 2, 3).forEach(System.out::println);
Stream.of(1, 2, 3).forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
閉包「Closure」
Java 閉包也是假的,和匿名類的限制一樣,要求閉包訪問的外部作用域變數是 final 的,實作應該都是:基礎型別值傳遞,參考型別參考傳遞,
以下 Java 代碼編譯會報錯:
int index = 0;
Stream.of("a", "b", "c")
.forEach(x -> System.out.println((index++) + ":" + x));
Error: Variable used in lambda expression should be final or effectively final
JS 閉包毫無問題:
let index = 0;
["a", "b", "c"].forEach(x => console.log(index++ + ":" + x));
盡管如此,函式式習慣還是可以帶到 Java 了,
第一個要帶過來的是:少量的資料結構搭配大量的操作,
在 OOP 的世界里,開發者被鼓勵針對具體的問題建立專門的資料結構,并以方法的形式,將專門的操作關聯在資料結構上,函式式編程語言選擇了另一種重用思路,它們用很少的一組關鍵資料結構( 如 list、 set、 map)來搭配專為這些資料結構深度優化過的操作,我們在這些關鍵資料結構和操作組成的一套運轉機構上面,按需要“ 插入” 另外的資料結構和高階函式來調整機器,以適應具體的問題,
Neal Ford. 函式式編程思維
在 Java 中,關鍵資料結構就是指 Stream,
Stream 操作三板斧:map、filter、reduce
Java 和 JS 有些不一樣,
不同點一:index 的取法
JS map、filter、reduce 都能拿到 index:
["a", "b", "c"]
.map((s, index) => `${index}: ${s}`)
.forEach(s => console.log(s));
Java 想要 index 的資訊,如果只是單執行緒,可以考慮利用參考型別保存迭代的索引:
AtomicInteger i = new AtomicInteger(0);
Stream.of("a", "b", "c")
.map(x -> i.getAndIncrement() + ":" + x)
.forEach(System.out::println);
如果想支持多執行緒,可以先通過 zip 讓流中的資料帶上 index:
public static <T, R> List<Pair<T, R>> zip(List<T> s1, List<R> s2) {
return IntStream.range(0, Math.min(s1.size(), s2.size()))
.mapToObj(index -> new Pair<T, R>(s1.get(index), s2.get(index)))
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
zip(IntStream.range(0, list.size()).boxed().collect(Collectors.toList()), list)
.parallelStream()
.map(p -> p.getKey() + ":" + p.getValue())
.forEach(System.out::println);
}
不同點二:reduce 的用法
Java 分為了 reduction 和 mutable reduction,在 JS 里是不區分的,
A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream.
-
JS
reduction:[1, 2, 3].reduce((acc, cur) => acc + cur, 0); -
Java
reduction:Stream.of(1, 2, 3).reduce(0, Integer::sum); -
JS
mutable reduction:[1, 2, 2].reduce((acc, cur) => { acc.add(cur); return acc; }, new Set()); -
Java
mutable reduction:Stream.of(1, 2, 2).collect( () -> new HashSet<>(), (set, el) -> set.add(el), (s1, s2) -> s1.addAll(s2) );也可以簡寫為:
Stream.of(1, 2, 2).collect( HashSet::new, HashSet::add, HashSet::addAll );引數比 JS 多了個
HashSet::addAll是為了并行處理,
后續繼續總結高階函式、柯里化、函子在 Java 中的應用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/507122.html
標籤:Java
