1. 引言
使用Lambda表达式的前提是需要有函数式接口,而Lambda使用时不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型,因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。这些函数式接口都定义在java.util.function中。
在学习内置函数式接口之前,先自己定义一个函数式接口
需求:定义一个函数式接口Operation,在测试类中定义一个方法test,参数就是接口Operation
接口:
package com.hm.funcinter;
public interface Operation {
void getSum(int a,int b);
}
测试类
package com.hm.funcinter.demo;
import com.hm.funcinter.Operation;
public class Demo01 {
public static void main(String[] args) {
test((a,b) -> System.out.println(a+b),3,5);
}
public static void test(Operation operation,int x,int y){
operation.getSum(x,y);
}
}
运行结果:8
2. 供给型接口——Supplier<T>
所谓供给型接口就是别人不给我们参数,但是我们返回一个类型值的接口方法,我们称之为供给型接口,也称之为生产者。
java.util.function.Supplier<T>接口仅包含一个无参的方法: T get() 。,对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。接口定义如下:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
通过Supplier的get方法,可以得到一个值,无参有返回的接口。
例如:使用Lambda表达式返回一个int数据中的最大值
package com.hm.funcinter.demo;
import java.util.Arrays;
import java.util.function.Supplier;
public class DemoSupplier {
public static void main(String[] args) {
int i = printMax(() -> {
int[] intArr = {1, 4, 2, 5, 6, 3};
Arrays.sort(intArr);
return intArr[intArr.length - 1];
});
System.out.println(i);
}
public static int printMax(Supplier<Integer> supplier){
return supplier.get();
}
}
6.2 消费型——Consumer<T>
所谓的消费型就是需要一个参数,但是不给返回值的接口方法,称之为消费型接口,也称之为消费者。
java.util.function.Consumer<T>接口它就是一个消费型的接口,其数据类型由泛型参数来确定。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
default Consumer<T> andThen(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
this.accept(var2);
var1.accept(var2);
};
}
}
从其定义可以看出,该接口有一个抽象方法accept和一个默认方法andThen。
6.2.1 抽象方法accept
Consumer接口的accept可以拿到accept方法参数传递过来的数据进行处理,是一个有参数无返回值的方法。
例如:使用lambda表达式将一个小写的字符串转成大写的字符串
public class DemoConsumer {
public static void main(String[] args) {
toUp(str->System.out.println(str.toUpperCase()),"abc");
}
public static void toUp(Consumer<String> consumer,String str){
consumer.accept(str);
}
}
6.2.2 默认方法addThen
如果一个方法的参数和返回值全部都是Consumer类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后在做一个操作,实现组合,而这个方法就是addThen。
例如:实现将一个字符串,先转成小写,在转成大写
package com.hm.funcinter.demo;
import java.util.function.Consumer;
public class DemoConsumer {
public static void main(String[] args) {
toLower2up(str -> System.out.println(str.toLowerCase()),
str->System.out.println(str.toUpperCase()),
"hello zhongGuo!");
}
public static void toLower2up(Consumer<String> consumer1,Consumer<String> consumer2,String str){
consumer1.andThen(consumer2).accept(str);
}
}
当然,我们也可以用两个accept方法实现,只需要在toLower2up方法中用consumer1和consumer2分别调用一次accept方法即可。
6.3 函数接口Function<T,R>接口
接收一个参数,返回另外一个类型。
java.util.function.Function<T,R>接口用于根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称之为后置条件,有参数有返回值。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
default <V> Function<V, R> compose(Function<? super V, ? extends T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return this.apply(var1.apply(var2));
};
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return var1.apply(this.apply(var2));
};
}
static <T> Function<T, T> identity() {
return (var0) -> {
return var0;
};
}
}
function接口有一个抽象方法apply以及其他的默认方法
6.3.1 抽象方法apply
function转换型接口,对apply方法传入的T类型的数据进行处理,返回R类型的结果,有参数有返回值。
例如:将字符串转换成整型
package com.hm.funcinter.demo;
import java.util.function.Function;
public class DemoFunction {
public static void main(String[] args) {
Integer num = str2Integer(str -> new Integer(str), "123");
System.out.println(num);
}
public static Integer str2Integer(Function<String,Integer> fun,String str){
return fun.apply(str);
}
}
6.3.2 Function的链式调用andThen
Function中有一个方法andThen,可以实现链式调用。
例如:需要使用Lambda表达式将字符串转成数字,第二个操作将这个数字乘以5
package com.bjc.jdk8.function;
import java.util.function.Function;
public class Demo05Function {
public static void main(String[] args) {
getNumber1(str -> {return Integer.parseInt(str);},num -> {return num * 5;},"20");
}
public static void getNumber1(Function<String,Integer> func1,Function<Integer,Integer> func2,String str){
Integer apply = func1.andThen(func2).apply(str);
System.out.printf("apply = " + apply);
}
}
6.4 BiFunction<T,R,U>
对于Function函数式接口而言,只有一个输入参数,那如果想传两个参数呢?这时就可以用BiFunction接口,其中的Bi是Bidirectional【双向】的简写
接口定义如下:
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T var1, U var2);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (t, u) -> {
return after.apply(this.apply(t, u));
};
}
}
例如:
BiFunction<String,String,String[]> func3 = (str1,str2) -> {
return str1.split(str2);
};
System.out.println(func3.apply("hellow","2"));
6.5 IntFunction<R>接口
定义:
@FunctionalInterface
public interface IntFunction<R> {
R apply(int var1);
}
例如:
IntFunction<Double> func = l -> l * 2d;
Double result = func.apply(100d);
System.out.println(result);
6.6 判断型Predicate接口
别人给一个数据,返回一个Boolean类型的数据。有时候,我们需要对某种类型的数据进行判断,从而得到一个Boolean值,这时候就可以使用java.util.function.Predicate<T>接口,该接口一般主要用于过滤一些符合条件的逻辑,除此之外还会提供三个defalut方法与一个static方法,具体请看源代码:
package java.util.function;
import java.util.Objects;
/**
* @since 1.8
*/
@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);
}
}
方法说明:
修饰符和类型 | 函数名 | 函数说明 |
default Predicate<T> | and(Predicate<? super T> other) | 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND |
static <T> Predicate<T> | isEqual(Object targetRef) | 返回一个谓词,根据equals判断两个参数是否相等 |
default Predicate<T> | negate() | 返回表示此谓词的逻辑否定的谓词 |
default Predicate<T> | or(Predicate<? super T> other) | 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或 |
boolean | test(T t) | 判断输入是否符合某个条件 |
6.6.1 抽象方法test
例如:使用Lambda判断一个人的名字如果超过了3个字就认为是很长的名字
对test方法的参数T进行判断,返回Boolean类型的结果,用于条件判断场景
package com.bjc.jdk8.function;
import java.util.function.Predicate;
public class DemoPredicate {
public static void main(String[] args) {
Boolean name = predicateName(str -> {
return str.length() > 3;
}, "张三");
System.out.println(name);
}
public static Boolean predicateName(Predicate<String> pre, String name){
return pre.test(name);
}
}
6.6.2 默认方法and
既然是条件判断接口,就会存在与、或、非三种逻辑关系,其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,就可以使用Predicate接口的default方法and,其JDK源码是:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> {
return this.test(t) && other.test(t);
};
}
使用Lambda表达式判断一个字符串中既包含W,也包含H
package com.bjc.jdk8.function;
import java.util.function.Function;
import java.util.function.Predicate;
public class Demo05Function {
public static void main(String[] args) {
predicateTestAnd1(str -> {
return str.contains("W");
}, str -> {
return str.contains("H");
}, "ABCDXWYHZ");
}
public static Boolean predicateTestAnd1(Predicate<String> pre1, Predicate<String> pre2,String str){
Boolean flag = pre1.and(pre2).test(str);
if(flag){
System.out.println("W和H都包含了。。。");
} else {
System.out.println("W和H没有都包含。。。");
}
return flag;
}
}
6.6.3 默认方法or
使用Lambda表达式判断一个字符串中包含W或者包含H
package com.bjc.jdk8.function;
import java.util.function.Function;
import java.util.function.Predicate;
public class Demo05Function {
public static void main(String[] args) {
predicateTestOr(str -> {
return str.contains("W");
}, str -> {
return str.contains("H");
}, "ABCDXWYHZ");
}
public static Boolean predicateTestAnd1(Predicate<String> pre1, Predicate<String> pre2,String str){
Boolean flag = pre1.or(pre2).test(str);
if(flag){
System.out.println("W和有一个被包含了");
} else {
System.out.println("W和H都没有。。。");
}
return flag;
}
}
6.6.4 默认方法negate
negate方法用于取反操作
例如:判断一个字符串中不包含w
package com.bjc.jdk8.function;
import java.util.function.Function;
import java.util.function.Predicate;
public class Demo05Function {
public static void main(String[] args) {
predicateNegate(str->{
return str.contains("W");
},"hello");
}
public static Boolean predicateNegate(Predicate<String> pre1,String str){
Predicate<String> negate = pre1.negate();
Boolean flag = negate.test(str);
if(flag){
System.out.println("不包含W");
}
return flag;
}
}
总结:所谓的Lambda表达式,其实就是一个简写的匿名内部类,其原理就是预先生成一个匿名内部类,当执行到函数式接口的方法的时候,在调用Lambda表达式进行逻辑计算。
例如:计算一个整形数组的和
package com.bjc.jdk8.objRef;
import java.util.function.Consumer;
public class Demo1 {
public static void main(String[] args) {
int[] arr = {1,2,4,5,6,2,3};
sumIntArr(a -> {
int sum = 0;
for (int i : a) {
sum += i;
}
System.out.println(sum);
},arr);
}
public static void sumIntArr(Consumer<int[]> consumer,int[] arr){
consumer.accept(arr);
}
}
在该例子中,main函数执行到sumIntArr函数,首先执行该函数,该函数有两个参数,一个是consumer接口,一个是int数组,程序将Lambda表达式赋给第一个参数consumer接口,将arr赋给第二个参数arr,程序调用consumer.accept就是调用Lambda表达式。然后,我们给accept传递的参数arr,就传给了Lambda的a
7. 函数式接口LongPredicate
使用test判断Long参数的用法
// 函数定义
public static List<Apple> getApplesByWeight(List<Apple> apples, LongPredicate predicate){
List<Apple> list = new ArrayList<>();
for(Apple a : apples){
if(predicate.test(a.getWeight())){
list.add(a);
}
}
return list;
}
// Lambda表达式调用
List<Apple> applesByWeight = getApplesByWeight(list, weight -> weight > 100);
8. 函数式接口 BiPredicate<T,U>
使用test判断两个参数的用法
// 方法定义
public static List<Apple> getApplesByColorAndWeight(List<Apple> apples, BiPredicate<String,Long> predicate){
List<Apple> list = new ArrayList<>();
for(Apple a : apples){
if(predicate.test(a.getColor(),a.getWeight())){
list.add(a);
}
}
return list;
}
// 方法调用
List<Apple> applesByColorAndWeight = getApplesByColorAndWeight(list, (c, w) -> c.equals("green") && w == 100);
9. JDK 1.8 新增加的函数接口
java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:
接口 | 描述 |
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果 |
BinaryOperator<T> | 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
BiPredicate<T,U> | 代表了一个两个参数的boolean值方法 |
BooleanSupplier | 代表了boolean值结果的提供方 |
Consumer<T> | 代表了接受一个输入参数并且无返回的操作 |
DoubleBinaryOperator | 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果 |
DoubleConsumer | 代表一个接受double值参数的操作,并且不返回结果 |
DoubleFunction<R> | 代表接受一个double值参数的方法,并且返回结果 |
DoublePredicate | 代表一个拥有double值参数的boolean值方法 |
DoubleSupplier | 代表一个double值结构的提供方 |
DoubleToIntFunction | 接受一个double类型输入,返回一个int类型结果。 |
DoubleToLongFunction | 接受一个double类型输入,返回一个long类型结果 |
DoubleUnaryOperator | 接受一个参数同为类型double,返回值类型也为double 。 |
Function<T,R> | 接受一个输入参数,返回一个结果。 |
IntBinaryOperator | 接受两个参数同为类型int,返回值类型也为int 。 |
IntConsumer | 接受一个int类型的输入参数,无返回值 。 |
IntFunction<R> | 接受一个int类型输入参数,返回一个结果 。 |
IntPredicate | 接受一个int输入参数,返回一个布尔值的结果。 |
IntSupplier | 无参数,返回一个int类型结果。 |
IntToDoubleFunction | 接受一个int类型输入,返回一个double类型结果 。 |
IntToLongFunction | 接受一个int类型输入,返回一个long类型结果 |
IntUnaryOperator | 接受一个参数同为类型int,返回值类型也为int 。 |
LongBinaryOperator | 接受两个参数同为类型long,返回值类型也为long。 |
LongConsumer | 接受一个long类型的输入参数,无返回值。 |
LongFunction<R> | 接受一个long类型输入参数,返回一个结果。 |
LongPredicate | R接受一个long输入参数,返回一个布尔值类型结果。 |
LongSupplier | 无参数,返回一个结果long类型的值。 |
LongToDoubleFunction | 接受一个long类型输入,返回一个double类型结果。 |
LongToIntFunction | 接受一个long类型输入,返回一个int类型结果。 |
LongUnaryOperator | 接受一个参数同为类型long,返回值类型也为long。 |
ObjDoubleConsumer<T> | 接受一个object类型和一个double类型的输入参数,无返回值。 |
ObjIntConsumer<T> | 接受一个object类型和一个int类型的输入参数,无返回值。 |
ObjLongConsumer<T> | 接受一个object类型和一个long类型的输入参数,无返回值。 |
Predicate<T> | 接受一个输入参数,返回一个布尔值结果。 |
Supplier<T> | 无参数,返回一个结果。 |
ToDoubleBiFunction<T,U> | 接受两个输入参数,返回一个double类型结果 |
ToDoubleFunction<T> | 接受一个输入参数,返回一个double类型结果 |
ToIntBiFunction<T,U> | 接受两个输入参数,返回一个int类型结果。 |
ToIntFunction<T> | 接受一个输入参数,返回一个int类型结果 |
ToLongBiFunction<T,U> | 接受两个输入参数,返回一个long类型结果。 |
ToLongFunction<T> | 接受一个输入参数,返回一个long类型结果。 |
UnaryOperator<T> | 接受一个参数为类型T,返回值类型也为T。 |