参考
https://www.cnblogs.com/linzhanfly/p/9686941.html
概述
consumer:函数可以作为方法入参
目的:在此之前方法的参数一直只能是具体的对象,比如int/string/obj等等,引入consumer之后,方法的参数可以是一个具体的函数了,曲线救国。源码之下无秘密。
这段代码,在main函数中创建了consumer对象,同时实现了consumer唯一的待实现方法accept,然后将它作为参数传给了test方法。
test方法接收到consumer后,中调用了consumer的accept方法,真正执行了accept方法,并给了他具体需要执行的参数。
public static void main(String[] args) {
//1. 声明consumer,它接收一个参数,但不返回任何值
Consumer<Integer> consumer = v -> {
v = v + 10;
System.out.println(v);
};
//3. 传递参数consumer
test(consumer);
}
//2. 定义一个方法,接收参数为consumer类型
public static final void test(Consumer<Integer> consumer) {
//4. 在方法内部调用consumer的具体方法,同时给consumer传入参数
for (int i = 0; i < 10; i++) {
if(i%2==0){
consumer.accept(i);
}
}
}
这段代码在test中遍历0-9,当遍历的数字%2==0时,给他+10,然后打印出来,运行结果如下:
10
12
14
16
18
还有点懵的同学再看看这段熟悉的代码片段,因为consumer这个接口只有一个待实现的接口方法,所以我们平时写list.foreach的时候可以直接写出lamda风格的实现类:list.foreach(v-> System::println);
如果你来定义这种接口的话,使用consumer来当入参,好处是不是不言自明了。
Predicate:条件使用者来定义
Predicate和consumer有异曲同工之处,也是通过接口来使函数可以接收一个“函数”作为入参,但他和consumer不同,他的作用是让传入者来决定判断的条件,还是上面那个例子:
public static void main(String[] args) {
Predicate<Integer> predicate = v -> v % 2 == 0;
predicateTest(predicate);
}
public static final void predicateTest(Predicate<Integer> predicate) {
for (int i = 0; i < 10; i++) {
if(predicate.test(i)){
System.out.println(i+10);
}
}
}
我们把test方法中的判断条件,换成了使用入参predicate的test方法。运行结果如下:
10
12
14
16
18
Predicate只有一个待实现的方法,所以一样可以写成lamda风格。
再拿arraylist中的源码来举例
这是arralist里的条件删除方法,接收一个Predicate作为入参,这样使用者可以任意定义要删除符合什么规则的元素,使删除规则和具体的删除及便利操作解藕。
它还包含一些其他的默认实现方法,你可以选择直接用,也可以选择覆盖他们,看下这些定义,有什么想法吗:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
Function:转换逻辑交给我
Function接收一个参数,同时返回另一个类型的出参,中间的操作逻辑由定义的人自定义,他也是只有一个待实现的方法。还是上面的例子:
public static void main(String[] args) {
Function<Integer, Integer> funciton = (n) -> n+10;
functionTest(funciton);
}
public static final void functionTest(Function<Integer, Integer> function) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.println(function.apply(i));
} else {
System.out.println(i);
}
}
}
如果i%2==0,则执行function的apply方法,给i加上10,执行效果如下:
10
1
12
3
14
5
16
7
18
9
这个感觉是不是让你想起了stream流的map方法:
Supplier:每次都给你一个新对象
你可以把它当成一个用来创建对象的工厂,每一次get方法的调用他都会给你返回一个你定义好逻辑创建的新对象;也可以用来做使用时才加载,因为定义Supplier的时候他是不会做任何操作的,只有等到调用get,他才会执行
public static void main(String[] args) {
supplierTest(()->"hello world");
}
public static final void supplierTest(Supplier<String> supplier) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.println(supplier.get());
} else {
System.out.println(i);
}
}
}
当i%==2时,返回一个“新”的字符串,hello world
hello world
1
hello world
3
hello world
5
hello world
7
hello world
9
参考:Optional.orElseGet(Supplier<? extends T>):
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
UnaryOperator: 同类型转换
UnaryOperator和function作用差不多,只不过他不接受入参和出参为不同类型,只能为同类型
private static void unaryOperator() {
UnaryOperator<Integer> unaryOperator = (n) -> n+10;
unaryOperatorTest(unaryOperator);
}
public static final void unaryOperatorTest(UnaryOperator<Integer> unaryOperator) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.println(unaryOperator.apply(i));
} else {
System.out.println(i);
}
}
}
可以参考arraylist的replaceall方法