Java8之函数式编程
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
1.Lambda表达式
如下案例所示,分别创建三个接口,每个接口中只有一个方法,通过lambda表达式重写接口中的方法,可极大的简化代码。当只有一个入参时,则无需写()
当表达式只有一行时则无需写{}
与return
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunc1{/**
* 相当于接口实现类
* 当接口只有一个方法时可用Lambda表达式简写接口实现
*/staticAa aa=()->System.out.println("打印Aa");staticAb ab=()->"返回Ab";staticAc ac= name-> name+":说话";staticAc ac2= name->{System.out.println("入参:"+ name);return name+": 我是Ac";};publicstaticvoidmain(String[] args){
aa.testAa();System.out.println(ab.testAb());System.out.println(ac.testAc("kenewstar"));System.out.println(ac2.testAc("k2"));}}interfaceAa{voidtestAa();}interfaceAb{StringtestAb();}interfaceAc{StringtestAc(String name);}
执行结果:
打印Aa
返回Ab
kenewstar:说话
入参:k2
k2: 我是Ac
任何 Lambda 表达式的基本语法是:
- 参数。 2. 接着
->
,可视为“产出”。 3. -> 之后的内容都是方法体。
2.方法引用
除了可以使用Lambda表达式重写接口方法外,还可使用方法引用的方式重写接口方法,使用前提也必须是该接口是一个函数式接口,即接口中只有一个方法(抽象方法),因为Java8允许接口中有静态方法和默认方法。
如下案例使用Lambda表达式与方法引用重写同一个方法。
/**
* 方法引用
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunc2{staticStringreturnStr(){return"call2";}staticvoidprintRunnable(){System.out.println("打印Runnable2");}publicstaticvoidmain(String[] args)throwsException{// Callable函数式接口,// Lambda表达式 无入参,有返参Callable<String> c=()->"call";System.out.println(c.call());// 方法引用重写接口// 调用了一个只有返回值的方法Callable<String> c2=TestFunc2::returnStr;System.out.println(c2.call());// Runnable函数式接口Runnable r=()->System.out.println("打印Runnable");
r.run();// 方法引用重写接口// 调用了一个无入参,无返参的方法Runnable r2=TestFunc2::printRunnable;
r2.run();}}
执行结果:
call
call2
打印Runnable
打印Runnable2
注意 : 方法引用中重写的方法必须与函数式接口的方法类型格式保持一致,入参返参的顺序及类型都必须一致,才可使用该方法引用。
Runnable接口的一个简化示例
匿名内部类 -> lambda表达式 -> 方法引用
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunc3{staticvoidrun(){System.out.println("方法引用");}publicstaticvoidmain(String[] args){// 使用匿名内部类的方式重写接口newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("匿名内部类");}}).start();// 使用Lambda方式重写接口newThread(()->System.out.println("Lambda表达式")).start();// 使用方法引用的方式newThread(TestFunc3::run).start();}}
执行结果:
匿名内部类
Lambda表达式
方法引用
未绑定的方法引用
未绑定的方法引用是指没有关联对象的普通(非静态)方法。 使用未绑定的引用时,我们必须先提供对象:
我们先来看一个例子:
/**
* @author @author kenewstar
* @date 2022/01/23
*/publicclassTestFun4{publicstaticvoidmain(String[] args){// Non-static method cannot be referenced from a static context// TestFa fa = MethodRef::test;TestFb fb=MethodRef::test;MethodRef ref=newMethodRef();System.out.println(fb.tf(ref));// 或者使用 对象::方法的方式TestFa fa= ref::test;System.out.println(fa.tf());}}classMethodRef{publicStringtest(){return"test";}}interfaceTestFa{Stringtf();}interfaceTestFb{Stringtf(MethodRef ref);}
我们看到上面无法将 **MethodRef::test **函数式引用绑定到TestFa 接口上,这是因为实际上还有另一个隐藏的参数:this 。 你不能在没有 MethodRef 对象的前提下调用 test() 。 因此,MethodRef::test 表示未绑定的方法引用,因为它尚未“绑定”到对象,如果使用ref::test 方式则等同于已绑定的方法引用。
使用未绑定的引用时,函数式方法的签名(接口中的单个方法)不再与方法引用的签名完全匹配,因此需要一个额外的MethodRef 参数对象。原因是:你需要一个对象来调用方法。
如果有多个参数,则默认第一个参数传递this 对象
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunc4{publicstaticvoidmain(String[] args){TestFc fc=MethodRef::test;MethodRef ref=newMethodRef();System.out.println(fc.tf(ref,"other"));}}classMethodRef{publicStringtest(String name){return"test + "+ name;}}interfaceTestFc{Stringtf(MethodRef ref,String other);}
构造函数引用
即通过构造方法的引用创建对象
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunc5{publicstaticvoidmain(String[] args){CreatePerson cp=Person::new;Person kenewstar= cp.create("kenewstar",22);System.out.println(kenewstar);}}interfaceCreatePerson{Personcreate(String name,int age);}classPerson{String name;int age;publicPerson(String name,int age){this.name= name;this.age= age;}}
方法引用的三种方式:
类名::方法名
对象名::方法名
构造方法名:: new
3.函数式接口
@FunctionalInterface 一个被该注解标注的接口则表示它是一个函数式接口,该接口中只允许一个抽象方法的存在,存在多个则会编译不通过。例如前面例子中所提到的Runnable,Callable接口都是函数式接口
Java8提供函数式接口
特征 | 函数式方法名 | 示例 |
---|---|---|
无参数; 无返回值 | Runnable run() | Runnable |
无参数; 返回类型任意 | Supplier get() | SupplierBooleanSupplierIntSupplierLongSupplierDoubleSupplier |
无参数; 返回类型任意 | Callable call() | Callable |
1 个参数; 无返回值 | Consumer accept() | ConsumerIntConsumerLongConsumerDoubleConsumer |
2 个参数 ;Consumer | BiConsumer accept() | BiConsumer<T, U> |
2 个参数 Consumer; 1 引用; 1 基本类型 | Obj类型Consumer accept() | ObjIntConsumer ObjLongConsumer ObjDoubleConsumer |
1 个参数; 返回类型不同 | Function apply() To类型 和 类型To类型 | Function IntFunction LongFunction DoubleFunction ToIntFunction ToLongFunction ToDoubleFunction IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
1个 参数; 返回类型相同 | UnaryOperator apply() | UnaryOperator IntUnaryOperator LongUnaryOperator DoubleUnaryOperator |
2 个参数类型相同; 返回类型相同 | Comparator compare() | Comparator |
2 个参数; 返回布尔型 | Predicate test() | Predicate BiPredicate IntPredicate LongPredicate DoublePredicate |
参数基本类型; 返回基本类型 | 类型To类型Function applyAs类型() | IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
2 个参数类型不同 | Bi操作 (不同方法名) | BiFunction BiConsumer BiPredicate ToIntBiFunction ToLongBiFunction ToDoubleBiFunction |
3.1 Supplier
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestSupplier{staticSupplier<String> s=()->"我是Supplier型接口";staticBooleanSupplier bs=()->true;staticIntSupplier is=()->100;staticLongSupplier ls=()->100L;staticDoubleSupplier ds=()->100.00;publicstaticvoidmain(String[] args){// Supplier类型System.out.println(s.get());// 我是Supplier型接口System.out.println(bs.getAsBoolean());// trueSystem.out.println(is.getAsInt());// 100System.out.println(ls.getAsLong());// 100System.out.println(ds.getAsDouble());// 100.0}}
3.2 Consumer
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestConsumer{staticConsumer<String> c= str->System.out.println("consumer "+ str);staticIntConsumer ic= i->System.out.println(i*100);staticLongConsumer lc= l->System.out.println(l*100);staticDoubleConsumer dc= d->System.out.println(d*100);publicstaticvoidmain(String[] args){// Consumer类型
c.accept("kns");// consumer kns
ic.accept(2);// 200
lc.accept(3L);// 300
dc.accept(4D);// 400.0}}
3.3 BiConsumer
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestBiConsumer{staticBiConsumer<String,Integer> bc=(str, i)->{Integer result;try{
result=Integer.parseInt(str);}catch(Exception e){
result=0;}System.out.println(result+ i);};publicstaticvoidmain(String[] args){
bc.accept("100",200);// 300}}
3.4 ObjConsumer
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestObjConsumer{staticObjIntConsumer<String> oic=(o, i)->System.out.println(o+ i);staticObjLongConsumer<String> olc=(o, l)->System.out.println(o+ l);staticObjDoubleConsumer<String> odc=(o, d)->System.out.println(o+ d);publicstaticvoidmain(String[] args){
oic.accept("obj",100);// obj100
olc.accept("obj",100L);// obj100
odc.accept("obj",100.0);// obj100.0}}
3.5 Function
/**
* @author kenewstar
* @date 2022/01/23
*/publicclassTestFunction{staticFunction<String,String> func= s-> s;// 将数字转换为字符串staticIntFunction<String> iFunc=String::valueOf;staticLongFunction<String> lFunc=String::valueOf;staticDoubleFunction<String> dFunc=String::valueOf;// 将字段串转换为数字类型staticToIntFunction<String> tif=Integer::parseInt;staticToLongFunction<String> tlf=Long::parseLong;staticToDoubleFunction<String> tdf=Double::parseDouble;// 将类型转换另一个类型staticIntToLongFunction ilf= i-> i;staticIntToDoubleFunction idf= i-> i;staticLongToIntFunction lif= l->(int)l;staticLongToDoubleFunction ldf= l-> l;staticDoubleToIntFunction dif= d->(int)d;staticDoubleToLongFunction dlf= d->(long)d;publicstaticvoidmain(String[] args){System.out.println(func.apply("abc"));System.out.println(iFunc.apply(100));System.out.println(lFunc.apply(200L));System.out.println(dFunc.apply(300.0));System.out.println(tif.applyAsInt("111"));System.out.println(tlf.applyAsLong("222"));System.out.println(tdf.applyAsDouble("333"));System.out.println(ilf.applyAsLong(100));System.out.println(idf.applyAsDouble(100));System.out.println(lif.applyAsInt(200L));System.out.println(ldf.applyAsDouble(200L));System.out.println(dif.applyAsInt(300.0));System.out.println(dlf.applyAsLong(300.0));}}
3.6 UnaryOperator
/**
* 继承自Function
* 入参返参相同
* @author kenewstar
* @date 2022/01/23
*/publicclassTestUnaryOperator{staticUnaryOperator<String> uo= s-> s;staticIntUnaryOperator iuo= i-> i*2;staticLongUnaryOperator luo= l-> l*2;staticDoubleUnaryOperator duo= d-> d*2;publicstaticvoidmain(