函数式接口
内置常用的函数式接口位于java.util.function.*包中。
常用函数式接口Function、consumer、predicate、supplier等。
Lambda表达式 && FunctionalInterface
接口由@FunctionalInterface注解标注,该注解仅仅为定义约束,不注解也不会影响功能(从code规范上应该要注解),只要满足函数式接口定义都属于函数式接口(接口有且只能有个一个抽象方法)。
函数式接口为普通接口增加了更严格的约定;
lambda表达式是匿名类的简化书写形式,但lambda只能用于函数式接口;
即lambda表达式只能基于函数式接口而存在,因此是类接口实例化的简化书写形式,更简洁、方便和灵活。
@FunctionalInterface
public interface PrintFunction<T> {
void print(T t);
}
上面定义了一个函数式接口,有且只有一个抽象方法。接口使用泛型T,便于实现不同类型的参数打印。
PrintFunction<String> pf = new PrintFunction<String>() {
@Override
public void print(String s) {
System.out.println(s);
}
};
pf.print("a"); //a
上面通过匿名类方式直接实现了接口并实例化,并使用方法打印“a”。
PrintFunction<String> pf = (s) -> {
System.out.println(s);
};
pf.print("b");
上面通过lambda方式实现了接口并实例化,并调用方法print打印“b”,(s) 为入参,大括号内是方法的实现。大括号结束有分号。由于只有一个语句,可以省略{},只有一个参数,可以省略()。即:s -> System.out.println(s) 。
对比看lambda也就是书写形式上简化了。也因为lambda的这种书写形式,所以要求接口有且仅有一个抽象方法,便于直接用 “->” 实现。
这个lambda表达式是基于PrintFunction接口的定义来写的,编译器会按照接口定义检查表达式书写是否正确。
PrintFunction<String> pf = System.out::println;
pf.print("b"); //b
上面代码是对lambda表达式更简化的书写,System.out是指定的类,println是指定的方法,该方法只有一个入参,正好接口方法只会传入一个入参,因此可以正好匹配。所以只需要写明具体类的具体方法即可,编译器会自动识别及解析出来。
lambda表达式一定是基于某个接口定义而书写的,其他书写格式可参考lambda语法。
Function
接收T对象,返回R对象。
先看源码(去掉了注释)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function接口包含两个default方法及一个static方法。
default方法compose:先执行before入参的函数,再执行自身函数;
default方法andThen:先执行自身函数,再执行after入参的函数。
Function<Integer, Integer> f = s -> s++;
Function<Integer, Integer> g = s -> s * 2;
/**
* f.apply( g.apply(1) )
*/
System.out.println(f.compose(g).apply(1)); // 2
使用该函数式接口时,先定义函数体,再调用该函数体方法执行(与匿名类只是书写形式不一样),lambda简洁的形式,让程序逻辑更灵活。
如上代码,f.compose(g),将g函数作为入参,与当前函数的方法一起构成了复杂的方法,该复杂的方法:入参v,方法体apply(before.apply(v))。即为:
Function<Integer,Integer> ff = (v) -> {
Integer s = v * 2; // g函数体
return s++; // f函数体
};
如上代码用匿名类实现如下,代码量明显较多,复杂的逻辑中不太灵活,如有3、5个以上的函数连贯使用,代码量就更多了。
Function<Integer ,Integer> fxx1 = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer s) {
return s * 2;
}
};
Function<Integer ,Integer> fxx2 = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer s) {
return s++;
}
};
System.out.println( fxx1.compose(fxx2).apply(1)); // 2
default的andThen方法:
/**
* g.apply( f.apply(1) )
*/
System.out.println(f.andThen(g).apply(1)); // 2
static的identify方法:
该方法返回一个Function接口的实例对象(Function f2 = t -> t;),该lambda表达式是一个Function接口的对象。
直接将入参返回,用处如下:
1、使用该静态方法,可以直接创建一个function对象;
2、函数式编程的很多方法定义都是需要传递Function接口的参数,使用该static方法能快速构建对象。
如下代码toMap方法,需要Function类型的入参,t -> t 可直接使用静态方法取代。
Stream<String> stream = Stream.of("a", "bbb", "cccc");
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
// map = stream.collect(Collectors.toMap(t -> t, String::length));
Consumer
消费型接口,即传入的参数,在函数体方法中消费,不会有返回值。
用于一些将信息传递到方法中,方法直接处理掉,无需返回的场景。
源码如下:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
default方法中andThen: 先执行自身的函数,再执行入参中的函数,可以用于链式处理。
default方法中andThen: 先执行自身的函数,再执行入参中的函数,可以用于链式处理。
Consumer<Integer> ccc = System.out::print;
ccc.andThen(t -> System.out.print("---c1:" + t))
.andThen(t -> System.out.print("---c2:" + t))
.andThen(t -> System.out.print("---c3:" + t))
.accept(1);
// 1---c1:1---c2:1---c3:12
Predicate
测试型函数,用于判断或计算等逻辑,最后返回ture 或 false 的场景。
源码如下:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// 两个逻辑型方法的 &&
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);
}
}
Predicate<String> pre = Predicate.isEqual("abc");
Boolean bo = pre.test("abc"); // true
Supplier
供应型函数,无需入参,每次调用即可返回信息T。
源码如下:
@FunctionalInterface
public interface Supplier<T> {
/**
* @return a result
*/
T get();
}
如下函数体S2,每次调用产生3位随机数。
Supplier<Integer> s2 = () -> {
return (int) (Math.random() * 1000);
};
System.out.println(s2.get());
BiFunction
接收两个入参且返回结果的函数,与Function对比,也就是多了一个入参,很多场景我们需要两个入参的情况。"Bi"是一个英语前缀,表示两个的意思(比如biennial,bi两,enn年,ial形容词后缀,两年生的)。
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BiConsumer
与Consumer功能一样,区别是可以传递两个入参
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
BiPredicate
与Predicate功能一样,区别是可以传递两个入参
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {
Objects.requireNonNull(other);
return (T t, U u) -> test(t, u) && other.test(t, u);
}
default BiPredicate<T, U> negate() {
return (T t, U u) -> !test(t, u);
}
default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {
Objects.requireNonNull(other);
return (T t, U u) -> test(t, u) || other.test(t, u);
}
}
UnaryOperator
入参出参类型相同,Unary:一元的(一元结构)。
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
BinaryOperator
二元操作函数,minBy静态方法接受Comparator接口的函数作为入参,最终函数为:传递两个参数进行二元比较,返回最小的一个参数,比较的方法体写在Comparator的函数体中。
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
int类型
IntBinaryOperator、
IntConsumer、
IntFunction、
IntPredicate、
IntSupplier、
IntUnaryOperator
@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}
double类型
DoubleBinaryOperator、
DoubleConsumer、
DoubleFunction、
DoublePredicate、
DoubleSupplier、
DoubleUnaryOperator
@FunctionalInterface
public interface DoubleFunction<R> {
R apply(double value);
}
long类型
LongBinaryOperator、
LongConsumer、
LongFunction、
LongPredicate、
LongSupplier、
LongUnaryOperator
@FunctionalInterface
public interface LongSupplier {
long getAsLong();
}
泛型对象与double、int、long混用类型
ObjDoubleConsumer、
ObjIntConsumer、
ObjLongConsumer
@FunctionalInterface
public interface ObjIntConsumer<T> {
void accept(T t, int value);
}
boolean类型参数
BooleanSupplier
@FunctionalInterface
public interface BooleanSupplier {
boolean getAsBoolean();
}
入参与出参类型转换
DoubleToIntFunction、
DoubleToLongFunction、
IntToDoubleFunction、
IntToLongFunction、
LongToDoubleFunction、
LongToIntFunction
@FunctionalInterface
public interface LongToIntFunction {
int applyAsInt(long value);
}
出参类型限定的函数
ToDoubleBiFunction、
ToDoubleFunction、
ToIntBiFunction、
ToIntFunction、
ToLongBiFunction、
ToLongFunction
@FunctionalInterface
public interface ToIntFunction<T> {
int applyAsInt(T value);
}
Stream
Stream是java 8 新特性,流式处理,将元素转换为流,流经过管道流出,在管道各节点可以进行筛选、排序等处理,在管道末端再进行聚合转换处理。
元素转换为流对象后,后续处理都可以返回流对象本身,多个操作即可串行执行,犹如数据在管道中流过。
//1、使用Stream.of方法构造流,再对流转换为List对象
List<String> strList = Stream.of("1", "5", "9", "2").collect(Collectors.toList());
//2、将list中数据过滤并转换为Integer类型
List<Integer> intList = strList.stream().map(Integer::parseInt).filter(i -> i > 3).collect(Collectors.toList());
//3、list中元素迭代打印
intList.stream().forEach(System.out::println); // 5 9
//4、list中元素求和计算
int sum = intList.stream().mapToInt(Integer::intValue).sum();
System.out.println(sum); // 14
- Stream中静态of方法构造流,返回Stream对象(该对象为实现了Stream接口的类的对象ReferencePipeline.Head,可通过源码找到该类)
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
Steam的collect方法需要传递一个Collector类型参数,将流转换到Collector类型指定的ArrayList对象并返回List对象。
<R, A> R collect(Collector<? super T, A, R> collector);
//语句的前面<R,A>为声明该方法用到了两个泛型类型,只是声明,泛型方法的语法约定先声明后使用。
Collectors.toList(),该方法返回一个CollectorImpl对象(该类父类为Collector接口)。
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
- strList.stream().map(Integer::parseInt)中的map要求传递的是Function类型参数,因此可以使用lambda构造;filter方法要求Predicate类型参数。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Stream<T> filter(Predicate<? super T> predicate);
- Stream中forEach迭代方法,可以迭代每个元素,如何处理,需要传递Consumer消费型函数处理。
void forEach(Consumer<? super T> action);
- 将流中元素转换为intStream,便于使用IntStream中相关运算的方法 count()、average()、 max()、min()、sum()等。
转换为int用到了ToIntFunction函数接口。
IntStream mapToInt(ToIntFunction<? super T> mapper);
并行和串行流
并行流即流中的元素可以并行处理,没有顺序。串行流则会按照顺序处理。
List<String> list = Stream.of("1","2","3").collect(Collectors.toList());
list.stream().forEach(System.out::println); // 1 2 3
list.parallelStream().forEach(System.out::println); // 2 3 1
:: 符号
前面已经多次出现了::号,用于简化lambda表达式,
Consumer<String> f0 = t -> System.out.println(t);
Consumer<String> f1 = System.out::println;
- ::符号前面为具体的对象或类,
- ::符号后面为该对象的方法或该类的静态方法;
- ::符号后面的方法的入参和出参必须与函数接口要求的出入参一致
满足上面3点即可使用::符号简化lambda表达式。
public class FunctionTest {
public static void main(String[] args) {
Consumer<String> f1 = System.out::println;
BiFunction<String, String, String> f2 = FunctionTest::bb;
Function<String, String> f3 = new FunctionTest()::aa;
}
public static String bb(String s, String v) {
System.out.println(s);
return s;
}
public String aa(String s) {
System.out.println(s);
return s;
}
}
- 如上f1函数,println没有返回值,正好与Consumer也没有返回值匹配。因此不能使用Function接口。
- f2中直接用了FunctionTest的类名,后面的方法必须是静态方法。
- 如果不是静态方法,则必须传入实例化对象,即f3函数格式。