Java8 进阶
文章目录
- Java8 进阶
- 什么是函数式接口?
- public interface Supplier
- public interface Consumer
- public interface Predicate
- public interface Function
- Java8 特性总结:
- 一、Function<T, R>
- 二、Consumer<T>
- 三、Supplier<T>
- 四、Predicate<T>
- Lambda表达式的基本结构:
什么是函数式接口?
首先,它还是一个接口,所以必须满足接口最基本的定义。但它是一个特殊的接口:SAM类型的接口(Single Abstract Method)。可以在调用时,使用一个lambda表达式作为参数。定义要求:
1. 只能有一个抽象方法需要被实现
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
备注:此处不包括与Object的public方法(clone方法不行,因为clone方法是protected,编译会报错)重名的方法。当然里面的默认方法、static方法都是无所谓的
default 修饰的默认方法方法,这个关键字是Java8中新增的,为的目的就是使得某一些接口,原则上只有一个方法被实现,但是由于历史原因,不得不加入一些方法来兼容整个JDK中的API,所以就需要使用default关键字来定义这样的方法
- default 修饰的默认方法方法,这个关键字是Java8中新增的,为的目的就是使得某一些接口,原则上只有一个方法被实现,但是由于历史原因,不得不加入一些方法来兼容整个JDK中的API,所以就需要使用default关键字来定义这样的方法
以下附JDK 8之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
Java8还提供了@FunctionalInterface注解来帮助我们标识函数式接口。所以Java8后上面那些接口都被打上了这个标记。下面给出一张图:说出Java8新提供的函数式接口们(可以满足99%需求):
四大核心函数式接口:
名称 | 一元接口 | 说明 | 二元接口 | 说明 |
---|---|---|---|---|
一般函数 | Funcation | 一元函数,抽象apply方法 | BiFunction | 二元函数,抽象apply方法 |
算子函数 | UnaryOperator | 一元函数,抽象apply方法 | BinaryOperator | 二元函数,抽象apply方法 |
谓词函数(输出boolean) | Predicate | 一元函数,抽象test方法 | BiPredicate | 二元函数,抽象test方法 |
消费者(无返回值) | Consumer | 一元函数,抽象accept方法 | BiConsumer | 二元函数,抽象accept方法 |
供应者(无参数,只有返回值) | Supplier | 供应者函数,抽象get方法 | - | - |
public interface Supplier
其简洁的声明,会让人以为不是函数。这个抽象方法的声明,同Consumer相反,是一个只声明了返回值,不需要参数的函数(这还叫函数?)。也就是说Supplier其实表达的不是从一个参数空间到结果空间的映射能力,而是表达一种生成能力。
Supplier<String> supplier = String::new;
其他Supplier扩展接口:
- BooleanSupplier : boolean getAsBoolean();返回boolean
- DobuleSupplier: double getAsDouble();返回double
- IntSupplier: int getAsInt();返回int
- LongSupplier: long getAsLong();返回long
public interface Consumer
这个接口声明太重要了,应用场景太多了。因为需要返回值的我们用Function,不需要返回值的,我们用它就可。
Consumer consumer = System.out::println;
看其源码 还有个默认方法andThen:
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
andThen可以实现消费两次。消费一次后,继续消费一次。使用场景:其他Consumer扩展接口:
-
BiConsumer:void accept(T t, U u);接受两个参数
-
DoubleConsumer:void accept(double value);接受一个double参数
-
IntConsumer:void accept(int value);接受一个int参数
-
LongConsumer:void accept(long value);接受一个long参数
-
ObjDoubleConsumer:void accept(T t, double value);接受一个泛型参数一个double参数
-
ObjIntConsumer:void accept(T t, int value);接受一个泛型参数一个int参数
-
ObjLongConsumer:void accept(T t, long value);接受一个泛型参数一个long参数
public interface Predicate
断言接口,有点意思了。其默认方法也封装了and、or和negate逻辑 和一个静态方法isEqual。
//and方法接收一个Predicate类型,也就是将传入的条件和当前条件以并且的关系过滤数据。
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//or方法同样接收一个Predicate类型,将传入的条件和当前的条件以或者的关系过滤数据
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
//negate就是将当前条件取反
default Predicate<T> negate() {
return (t) -> !test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
看几个案例:
public List<Integer> conditionFilterAnd(List<Integer> list, Predicate<Integer> predicate,Predicate<Integer> predicate2){
return list.stream().filter(predicate.and(predicate2)).collect(Collectors.toList());
}
public List<Integer> conditionFilterOr(List<Integer> list, Predicate<Integer> predicate,Predicate<Integer> predicate2){
return list.stream().filter(predicate.or(predicate2)).collect(Collectors.toList());
}
public List<Integer> conditionFilterNegate(List<Integer> list, Predicate<Integer> predicate){
return list.stream().filter(predicate.negate()).collect(Collectors.toList());
}
//大于5并且是偶数
result = predicateTest.conditionFilterAnd(list, integer -> integer > 5, integer1 -> integer1 % 2 == 0);
result.forEach(System.out::println);//6 8 10
System.out.println("-------");
//大于5或者是偶数
result = predicateTest.conditionFilterOr(list, integer -> integer > 5, integer1 -> integer1 % 2 == 0);
result.forEach(System.out::println);//2 4 6 8 9 10
System.out.println("-------");
//条件取反
result = predicateTest.conditionFilterNegate(list,integer2 -> integer2 > 5);
result.forEach(System.out::println);// 1 2 3 4 5
System.out.println("-------");
最后再来看一下Predicate接口中的唯一一个静态方法(小纵范围使用):
isEqual方法返回类型也是Predicate,也就是说通过isEqual方法得到的也是一个用来进行条件判断的函数式接口实例。而返回的这个函数式接口实例是通过传入的targetRef的equals方法进行判断的。我们看一下具体
public static void main(String[] args) {
System.out.println(Predicate.isEqual("test").test("test")); //true
System.out.println(Predicate.isEqual(null).test("test")); //false
System.out.println(Predicate.isEqual(null).test(null)); //true
System.out.println(Predicate.isEqual(1).test(new Integer(1))); //true
//注意 这里是false的
System.out.println(Predicate.isEqual(new Long(1)).test(new Integer(1))); //false
}
其他Predicate扩展接口:
-
BiPredicate:boolean test(T t, U u);接受两个参数的,判断返回bool
-
DoublePredicate:boolean test(double value);入参为double的谓词函数
-
IntPredicate:boolean test(int value);入参为int的谓词函数
-
LongPredicate:boolean test(long value);入参为long的谓词函数
public interface Function
这个接口非常非常总要。是很上层的一个抽象。除了一个抽象方法apply外,其默认实现了3个default方法,分别是compose、andThen和identity。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
compose 和 andThen 的不同之处是函数执行的顺序不同。andThen就是按照正常思维:先执行调用者,再执行入参的。然后compose 是反着来的,这点需要注意。看看唯一的一个静态方法identity:
static <T> Function<T, T> identity() {
return t -> t;
}
我们会发现,identity啥都没做,只是返回了一个Function方法,并且是两个泛型都一样的方法,意义着实不是太大。下面看一个复杂点的例子,各位感受一下:
public static void main(String[] args) {
Function<Integer, Integer> times2 = i -> i * 2; //加倍函数
Function<Integer, Integer> squared = i -> i * i; //平方函数
System.out.println(times2.apply(4)); //8
System.out.println(squared.apply(4)); //16
System.out.println(times2.compose(squared).apply(4)); //32 先4×4然后16×2, 先执行参数,再执行调用者
System.out.println(times2.andThen(squared).apply(4)); //64 先4×2,然后8×8, 先执行调用者,再执行参数
//看看这个例子Function.identity()构建出一个恒等式函数而已,方便方法的连缀 这就是它的唯一优点
System.out.println(Function.identity().compose(squared).apply(4)); //16 先执行4*4,再执行identity 值不变
}
Java8 特性总结:
一、Function<T, R>
T:入参类型;R 出参
调用方法:
R apply(T t);
定义函数示例:
Function<Integer, Integer> func = p -> p * 10; // 输出入参的10倍
调用函数示例:
func.apply(10); // 结果100
二、Consumer
T:入参类型;void 没有出参
调用方法:
void accept(T t);
定义函数示例:
Consumer<String> consumer= p -> System.out.println(p); // 因为没有出参,常用于打印、发送短信等消费动作
调用函数示例:
consumer.accept("18800008888");
三、Supplier
T:出参类型;void没有入参
调用方法:
T get();
定义函数示例:
Supplier<Integer> supplier= () -> 100; // 常用于业务“有条件运行”时,符合条件再调用获取结果的应用场景;运行结果须提前定义,但不运行。
调用函数示例:
supplier.get();
四、Predicate
T:入参类型;出参类型是Boolean
调用方法:
boolean test(T t);
定义函数示例:
Predicate<Integer> predicate = p -> p % 2 == 0; // 判断是否、是不是偶数
调用函数示例:
predicate.test(100); // 运行结果true
函数名称 | 入参 | 出参 | 描述 | |
---|---|---|---|---|
Function | T | R | 接收一个输入参数,返回一个结果。参数与返回值的类型可以不同,我们之前的map方法内部lambda就是表示这个函数式接口的; | public interface Function<T, R> { R apply(T t); } |
Predicate | T | Boolean | 接收一个输入参数,返回一个布尔值结果。比如我在对数据流中的元素进行筛选的时候,就可以基于Predicate的lambda; | public interface Predicate { boolean test(T t); } |
Consumer | T | Void | 接收一个输入参数并且无返回参数。 比如我们针对数据流的每一个元素进行打印,就可以基于Consumer的lambda; | public interface Consumer { void accept(T t); } |
Supplier | Void | T | 无需输入参数,值返回结果。看接口名就知道是发挥了对象工厂的作用; | public interface Supplier { T get(); } |
Lambda表达式的基本结构:
- 一个Lambda表达式可以有0个或多个参数,参数的类型可以明确声明,也可以通过上下文来推断。例如(int a)和(a)效果一样;
- 所有参数都必须包含在圆括号内,参数之间用逗号相隔;
- 空圆括号代表参数集为空。例如:()-> 42
- 当只有一个参数,且其类型可以推导出时,圆括号()可以省略。例如:a -> return a* a
- Lambda表达式的主体也就是body可以包含0条或多条语句。
- 如果表达式的主体只有一条语句,花括号{}可以省略,匿名函数的返回类型与该主体表达式一致
- 如果表达式的主体包含一条语句以上,则必须包含在花括号{}里面形成代码块。匿名函数的返回类型与该主体表达式一致,若没有返回则为空。
- statement和expression的区别,expression只有一句,不需要花括号包裹,不需要return;statement需要花括号包裹,且如果有返回值,必须return