Table of contents
Functional interface SAM (single Abstract Method)
CPS(continuation-passing style)
lambda expression
Using lambda expressions first requires an interface:
Functional interface SAM (single Abstract Method)
@FunctionInterface //【非必须】,如果加上,编译器会进行校验
public interface Runnerable{ //【必须是】 interface
public abstract void run(); //【必须】单个非默认方法
}
Runnable r = ()->System.out.println("hello"); //对方法进行实现
Built-in common functional interface
enter | return value | class | method |
---|---|---|---|
T | R | Function<T,R> | R apply(T t) |
void | T | Supplier<T> | T get() |
T | void | Consumer<T> | void accept(T t) |
void | void | Runnalbe | void run() |
T | Boolean | Predicate<T> | boolean test(T t) |
Example:
// 一个输入,一个返回值:Function<T,R>
private static Function<Integer, String> int2String
= (Integer i) -> {return String.valueOf(i);};
// 语法糖
//只有一个表达式,可省略大括号
private static Function<Integer, String> int2String_1
= (Integer i) -> String.valueOf(i);
//参数类型可推导,可省略
private static Function<Integer, String> int2String_2
= (i) -> String.valueOf(i);
//类型可推导且只有一个参数,可省略参数括号
private static Function<Integer, String> int2String_3
= i -> String.valueOf(i);
private static Supplier<Integer> supplier = () -> 1;
private static Consumer<?> consumer = a -> {};
private static Predicate<?> predicate = a -> true;
function call
lambda is an expression type and cannot be called directly. It must call a method to call
@FunctionalInterface
public interface Runner {
public abstract void run();
}
public class TestRunner {
public static void main(String[] args) {
Runner runner = ()->System.out.println("this is a test");
runner.run();//调用接口的 run() 方法
}
}
// 输出:this is a test
method reference
· Static method reference -> needs to tell the class it belongs to classX::methodName
Constructor reference -> Need to tell the class it belongs to classX:: new
· Specify the method reference of the specific instance -> need to inform the instance it belongs to instance::methodName
Example 1: Static method
private Function<Integer, String> staticRef = LambdaBasic::int2StringFn;
public static String int2StringFn(Integer i) {
return String.valueOf(i);
}
System.out.println(staticRef.apply(1));
// 相当于:LambdaBasic.int2StringFn(1)
Example 2: Construction method
private static Supplier<LambdaBasic> constructor = LambdaBasic::new;
public LambdaBasic() {}
LambdaBasic lb = constructor.get();//相当于:new LambdaBasic ()
System.out.println(lb.staticRef.apply(1));//LambdaBasic.int2StringFn(1)
Example 3: Methods of Concrete Instances
private BiFunction<Integer, Integer, String> instanceRef = this::twoint2String;
public String twoint2String(int x, int y) {
return x + " " + y;
}
System.out.println(instanceRef.apply(1,2));
// 相当于:LambdaBasic.twoint2String(1,2)
/*
* @param <T> the type of the first argument to the function
* @param <U> the type of the second argument to the function
* @param <R> the type of the result of the function
*/
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
Example 4: Any instance method reference of the specified type [special]
public class T{
// ……
tMethod(arg2...argN)
// ……
}
public interface FN{
apply(T arg1, arg2...argN)
}
Function f(T::tMethod)
f.apply(Tinstance, arg2...argN)
// 相当于:
Tinstance.tMethod(arg2...argN)
// 最常用的例子:
class A{
Stringg getId();
}
Function f = A::getId();
f.apply(new A());//相当于:new A().getId()
CPS(continuation-passing style)
void fn1(...arg, Consumer callbakc){
res = arg....;
callback.accept(res);
//相当于回调函数,将入参结果作为参数传给回调函数
}
public class CPSSample {
public static int times (int v1, int v2) {
return v1 * v2;
}
public static void add (int v1, int v2, Consumer<Integer> kont) {
int ret = v1 + v2;
kont.accept(ret);
}
public static void square (int v1, Consumer<Integer> kont) {
kont.accept(v1 * v1);
}
public static void main(String[] args) {
add(1,2, ret1->{ // 将入参1,2的计算结果,作为参数给 Consumer 回调函数
square(ret1, ret2->{ // 将入参ret1的计算结果,作为参数给回调函数【相当于连续回调】
System.out.println(ret2);
});
});
}
}
Stream
The three most important functions:
· filter(Predicate predicate)
· map(Function mapper)
· reduce(U identity, BinaryOperator acc)
reduce understands:
/**
* reduce(U identity, BinaryOperator acc)
* 此方法用于理解 reduce:具体的计算逻辑是在函数 accumulator.apply()中实现
* @param initValue 初始值
* @param accumulator 一个函数
* @param datas (此次用集合模拟 stream 流输入)
* @return 返回 reduce 计算后的结果
*/
public static <T,R> R reduce(R initValue,
BiFunction<R, T, R> accumulator,
Collection<T> datas){
R ret = initValue;
for (T data : datas) {
ret = accumulator.apply(ret, data);
}
return ret;
}
reduce combat 1: summation
//list 相当于流,作为数据源
public static int sum(Collection<Integer> list) {
return list.stream().reduce(0, (acc, curr) -> acc + curr);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("sum = " + sum(list));//输出:sum = 21
}
Reduce combat 2: find the maximum/minimum value
public static Integer max(Collection<Integer> list) {
return list.stream().reduce(Math::max).orElse(null);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("max = " + max(list));//输出:max = 7
}
Reduce combat 3: concatenated into strings
/**
* 串联成字符串
* @param list 相当于流,模拟数据源
* @param delimiter 字符之间的分隔符
* @return 返回包含分隔符的串联起来的字符串
*/
public static String join(Collection<Integer> list, String delimiter) {
return list.stream().map(String::valueOf).reduce((acc, curr)-> acc + delimiter + curr).orElse(null);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("join = " + join(list, "|"));//输出:join = 2|3|4|7|5
}
Reduce combat 4: implement map with reduce
//R newValue = mapFn.apply(curr) : 通过mapFn.apply(curr)函数实现映射
public static <T,R> List<R> map(List<T> list, Function<T,R> mapFn) {
List<R> ret = new ArrayList<>();
return list.stream().reduce(ret, (acc, curr)->{
R newValue = mapFn.apply(curr);//!!!
acc.add(newValue);
return acc;
},
(list1, list2) -> {list1.addAll(list2); return list1;} );
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
List<Integer> list1 = filter(list, i->i>3);
System.out.println("filter = " + list1); //输出:filter = [4, 7, 5]
}
Reduce combat 5: implement filter with reduce
//p.test(curr) : 通过p.test(curr)函数实现判断过滤
public static <T, R> List<T> filter(List<T> list, Predicate<T> p) {
List<T> ret = new ArrayList<>();
return list.stream().reduce(ret, (acc, curr)->{
if (p.test(curr)) {
acc.add(curr);
}
return acc;
},
(list1, list2) -> {list1.addAll(list2); return list1;} );
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
List<Integer> list2 = map(list, i->i*2);
System.out.println("map = " + list2); //输出:map = [4, 6, 8, 14, 10]
}
Stream.collect()
The difference between colletc and reduce?
reduce operates on immutable data
collect operates on variable data
There are mainly two APIs:
collect(Supplier, Accumulator, Combiner)
collect(Collector)
Let's see what Collector is:
public interface Collector<T, A, R> {
/**
* @返回一个函数,该函数返回一个新的、可变结果的容器
*/
Supplier<A> supplier();
/**
* @返回一个函数,该函数将值折叠到可变结果的容器中
*/
BiConsumer<A, T> accumulator();
/**
* @返回一个函数,该函数将两个部分结果组合为一个组合结果
*/
BinaryOperator<A> combiner();
/**
* @返回一个函数,将中间结果转换为最近结果
*/
Function<A, R> finisher();
...
}
Collector mainly includes four methods. It can be said that collect(Collector) is the complete version of collect(Supplier, Accumulator, Combiner) .
In order to facilitate the construction of Collector, jdk provides some Collector APIs :
• toList / to(Concurrent)Map / toSet / toCollection
• counting / averagingXX / joining /summingXX
• groupBy / partitioningBy
• mapping / reducing
Collect practice 1:
Group by programming language first, then stratify by program level, and return a tuple <average salary, list of programmers>
//key:language v:(key:level, [salary, list])
public static Map<String, Map<Integer, Turple<Integer, List<Programmer>>>> classify(List<Programmer> programmers) {
return programmers.stream().collect(
Collectors.groupingBy(Programmer::getLanguage, //程序语言分组
Collectors.groupingBy(Programmer::getLevel, //等级分组
Collectors.collectingAndThen(
Collectors.toList(), //分组后结果生成list
list -> new Turple(list.stream().collect(Collectors.averagingInt(Programmer::getSalary)), list)
//对 list 中指定条件求平均值
)
)
));
}
Collect combat 2:
Randomly create a list of programmers:
(1) Create a stream with a specified length
(2) Create an object using a constructor for each number
(3) Randomly assign values to each field of each object
(4) Filter elements outside the data range
(5) Sort by specified field
(6) Produce to list
public static void main(String[] args) {
List<Programmer> programmers = Util.generate(10);
System.out.println(classify(programmers));
}
public static List<Programmer> generate(int count){
Random random = new Random();
return IntStream.rangeClosed(0, count) // 0..N (1)
.boxed() //装箱,将 int 装箱成 Integer
.map(S2F(Programmer::new))//Programmer::new 的方法签名为 Supplier,map 入参为 Function
// .map 之后,相当于把一个数字转换成为一个对象 (2)
.peek( //给对象添加属性 (3)
apply(Programmer::setLevel, apply(random::nextInt, 3))
//p.setLevel(random.nextInt(3))
.andThen(apply(Programmer::setSalary, apply(random::nextInt, 50000)))
.andThen(apply(Programmer::setName, ()-> "Programmer" + random.nextInt(count*10)))
.andThen(apply(Programmer::setOutput, apply(random::nextInt, 10)))
.andThen(apply(Programmer::setLanguage, apply(Util::randomPick,new String[]{"JAVA","JS","Lisp", "Haskell"})))
)
//.peek(System.out::println)
.filter(p->p.getSalary() >=3000 && p.getLevel() > 0) //(4)
.sorted(Comparator.comparing(Programmer::getLevel)) //(5)
.peek(System.out::println)
.collect(Collectors.toList()) //(6)
;
}
private static <T, INSTANCE> Consumer<INSTANCE> apply(BiConsumer<INSTANCE, T> consumer, Supplier<T> valueSupplier) {
return instance ->consumer.accept(instance, valueSupplier.get());
}
public static <I,O> Supplier<O> apply(Function<I,O> fn, I arg){
return ()-> fn.apply(arg);
}
Optional
Reference: Understanding, learning and using OPTIONAL in JAVA [ Understanding, learning and using Optional in Java - Zhang Boyu - Blog Garden ]
main effect:
① Solve the infamous NullPointerException (NullPointerException)
② Chain call
static class Person{
private Debit debit;
public Debit getDebit() {return debit;}
public void setDebit(Debit debit) {this.debit = debit;}
}
static class Debit{
private Card card;
public Card getCard() {return card;}
public void setCard(Card card) {this.card = card;}
}
static class Card{
private Bank bank;
public Bank getBank() {return bank;}
public void setBank(Bank bank) {this.bank = bank;}
}
static class Bank{
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
// 一般:每一层都需要判空
public static String getDebitBankName_1(Person person) {
if (person.getDebit()!=null) {
Debit debit = person.getDebit();
if (debit!=null) {
Card card = debit.getCard();
if (card != null) {
Bank bank = card.getBank();
if (bank != null) {
return bank.getName();
}
}
}
return null;
}else {
return null;
}
}
// optional 封装后:相当于装到一个盒子里,对外就是一个盒子,没有空的概念
public static String getDebitBankName_2(Person person) {
return Optional.ofNullable(person.getDebit())
.map(Debit::getCard) // debit->debit.getCard()
.map(Card::getBank)
.map(Bank::getName)
.orElse(null);
}
//进一步解耦(目前我也没完全搞明白)
public static String getDebitBankName_22(Person person) {
Function<Optional, Optional> getCard = Util.opMap(Debit::getCard);
Function<Optional, Optional> getBank = Util.opMap(Card::getBank);
Function<Optional, Optional> getName = Util.opMap(Bank::getName);
return (String) getCard.andThen(getBank).andThen(getName)
.apply(Optional.ofNullable(person.getDebit())).orElse(null);
}
public static <T,R> Function<Optional, Optional> opMap(Function<T, R> fn){
return op -> op.map(fn);
}
Functional programming is relatively flexible as a whole, and you need to watch and practice more to master it. I feel that I only understand it at the moment. Record it here, remind yourself to look at it later.