应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

三分钟读懂Java函数式编程(FP)的意义所在

  概述背景
  函数式编程的理论基础是阿隆佐丘奇(AlonzoChurch)于1930年代提出的演算(LambdaCalculus)。演算是一种形式系统,用于研究函数定义、函数应用和递归。它为计算理论和计算机科学的发展奠定了基础。随着Haskell(1990年)和Erlang(1986年)等新一代函数式编程语言的诞生,函数式编程开始在实际应用中发挥作用。函数式的价值
  随着硬件越来越便宜,程序的规模和复杂性都在呈线性的增长。这一切都让编程工作变得困难重重。我们想方设法使代码更加一致和易懂。我们急需一种语法优雅,简洁健壮,高并发,易于测试和调试的编程方式,这一切恰恰就是函数式编程(FP)的意义所在。
  函数式语言已经产生了优雅的语法,这些语法对于非函数式语言也适用。例如:如今Python,Java8都在吸收FP的思想,并且将其融入其中,你也可以这样想:
  OO(objectoriented,面向对象)是抽象数据,FP(functionalprogramming,函数式编程)是抽象行为。新旧对比
  用传统形式和Java8的方法引用、Lambda表达式分别演示。代码示例:interfaceStrategy{Stringapproach(Stringmsg);}classSoftimplementsStrategy{publicStringapproach(Stringmsg){returnmsg。toLowerCase()?;}}classUnrelated{staticStringtwice(Stringmsg){returnmsgmsg;}}publicclassStrategize{Strategystrategy;Stringmsg;Strategize(Stringmsg){strategynewSoft();〔1〕构建默认的Softthis。msgmsg;}voidcommunicate(){System。out。println(strategy。approach(msg));}voidchangeStrategy(Strategystrategy){this。strategystrategy;}publicstaticvoidmain(String〔〕args){Strategy〔〕strategies{newStrategy(){〔2〕Java8以前的匿名内部类publicStringapproach(Stringmsg){returnmsg。toUpperCase()!;}},msgmsg。substring(0,5),〔3〕基于Ldmbda表达式,实例化interfaceUnrelated::twice〔4〕基于方法引用,实例化interface};StrategizesnewStrategize(Hellothere);s。communicate();for(StrategynewStrategy:strategies){s。changeStrategy(newStrategy);〔5〕使用默认的Soft策略s。communicate();〔6〕每次调用communicate()都会产生不同的行为}}}
  输出结果:hellothere?HELLOTHERE!HelloHellothereHellothereLambda表达式
  Lambda表达式是使用最小可能语法编写的函数定义:(原则)Lambda表达式产生函数,而不是类Lambda语法尽可能少,这正是为了使Lambda易于编写和使用
  Lambda用法:interfaceDescription{Stringbrief();}interfaceBody{Stringdetailed(Stringhead);}interfaceMulti{StringtwoArg(Stringhead,Doubled);}publicclassLambdaExpressions{staticBodybodhhNoParens!;〔1〕一个参数时,可以不需要扩展(),但这是一个特例staticBodybod2(h)hMoredetails;〔2〕正常情况下的使用方式staticDescriptiondesc()Shortinfo;〔3〕没有参数的情况下的使用方式staticMultimult(h,n)hn;〔4〕多参数情况下的使用方式staticDescriptionmoreLines(){〔5〕多行代码情况下使用{}return关键字(在单行的Lambda表达式中return是非法的)System。out。println(moreLines());returnfrommoreLines();};publicstaticvoidmain(String〔〕args){System。out。println(bod。detailed(Oh!));System。out。println(bod2。detailed(Hi!));System。out。println(desc。brief());System。out。println(mult。twoArg(Pi!,3。14159));System。out。println(moreLines。brief());}}
  输出结果:Oh!NoParens!Hi!MoredetailsShortinfoPi!3。14159moreLines()frommoreLines()
  总结:Lambda表达式通常比匿名内部类产生更易读的代码,因此我们将尽可能使用它们。方法引用
  方法引用由类名或者对象名,后面跟着,然后跟方法名称,
  使用示例:interfaceCallable{〔1〕单一方法的接口(重要)voidcall(Strings);}classDescribe{voidshow(Stringmsg){〔2〕符合Callable接口的call()方法实现System。out。println(msg);}}publicclassMethodReferences{staticvoidhello(Stringname){〔3〕也符合call()方法实现System。out。println(Hello,name);}staticclassDescription{Stringabout;Description(Stringdesc){aboutdesc;}voidhelp(Stringmsg){〔4〕静态类的非静态方法System。out。println(aboutmsg);}}staticclassHelper{staticvoidassist(Stringmsg){〔5〕静态类的静态方法,符合call()方法System。out。println(msg);}}publicstaticvoidmain(String〔〕args){DescribednewDescribe();Callablecd::show;〔6〕通过方法引用创建Callable的接口实现c。call(call());〔7〕通过该实例call()方法调用show()方法cMethodReferences::hello;〔8〕静态方法的方法引用c。call(Bob);cnewDescription(valuable)::help;〔9〕实例化对象的方法引用c。call(information);cHelper::assist;〔10〕静态方法的方法引用c。call(Help!);}}
  输出结果:call()Hello,BobvaluableinformationHelp!Runnable接口
  使用Lambda和方法引用改变Runnable接口的写法:方法引用与Runnable接口的结合使用classGo{staticvoidgo(){System。out。println(Go::go());}}publicclassRunnableMethodReference{publicstaticvoidmain(String〔〕args){newThread(newRunnable(){publicvoidrun(){System。out。println(Anonymous);}})。start();newThread(()System。out。println(lambda))。start();newThread(Go::go)。start();通过方法引用创建Runnable实现的引用}}
  输出结果:AnonymouslambdaGo::go()未绑定的方法引用
  使用未绑定的引用时,需要先提供对象:未绑定的方法引用是指没有关联对象的普通方法classX{Stringf(){returnX::f();}}interfaceMakeString{Stringmake();}interfaceTransformX{Stringtransform(Xx);}publicclassUnboundMethodReference{publicstaticvoidmain(String〔〕args){MakeStringspX::f;〔1〕你不能在没有X对象参数的前提下调用f(),因为它是X的方法TransformXspX::f;〔2〕你可以首个参数是X对象参数的前提下调用f(),使用未绑定的引用,函数式的方法不再与方法引用的签名完全相同XxnewX();System。out。println(sp。transform(x));〔3〕传入x对象,调用x。f()方法System。out。println(x。f());同等效果}}
  输出结果:X::f()X::f()
  我们通过更多示例来证明,通过未绑的方法引用和interface之间建立关联:packagecom。github。xiao2shiqi。lambda;未绑定的方法与多参数的结合运用classThis{voidtwo(inti,doubled){}voidthree(inti,doubled,Strings){}voidfour(inti,doubled,Strings,charc){}}interfaceTwoArgs{voidcall2(Thisathis,inti,doubled);}interfaceThreeArgs{voidcall3(Thisathis,inti,doubled,Strings);}interfaceFourArgs{voidcall4(Thisathis,inti,doubled,Strings,charc);}publicclassMultiUnbound{publicstaticvoidmain(String〔〕args){TwoArgstwoargsThis::two;ThreeArgsthreeargsThis::three;FourArgsfourargsThis::four;ThisathisnewThis();twoargs。call2(athis,11,3。14);threeargs。call3(athis,11,3。14,Three);fourargs。call4(athis,11,3。14,Four,Z);}}构造函数引用
  可以捕获构造函数的引用,然后通过引用构建对象classDog{Stringname;intage1;ForunknownDog(){namestray;}Dog(Stringnm){namenm;}Dog(Stringnm,intyrs){namenm;ageyrs;}}interfaceMakeNoArgs{Dogmake();}interfaceMake1Arg{Dogmake(Stringnm);}interfaceMake2Args{Dogmake(Stringnm,intage);}publicclassCtorReference{publicstaticvoidmain(String〔〕args){通过::new关键字赋值给不同的接口,然后通过make()构建不同的实例MakeNoArgsmnaDog::new;〔1〕将构造函数的引用交给MakeNoArgs接口Make1Argm1aDog::new;〔2〕Make2Argsm2aDog::new;〔3〕Dogdnmna。make();Dogd1m1a。make(Comet);Dogd2m2a。make(Ralph,4);}}总结方法引用在很大程度上可以理解为创建一个函数式接口的实例方法引用实际上是一种简化Lambda表达式的语法糖,它提供了一种更简洁的方式来创建一个函数式接口的实现在代码中使用方法引用时,实际上是在创建一个匿名实现类,引用方法实现并且覆盖了接口的抽象方法方法引用大多用于创建函数式接口的实现函数式接口Lambda包含类型推导Java8引入java。util。function包,解决类型推导的问题
  通过函数表达式创建Interface:使用FunctionalInterface注解强制执行此函数式方法模式FunctionalInterfaceinterfaceFunctional{Stringgoodbye(Stringarg);}interfaceFunctionalNoAnn{Stringgoodbye(Stringarg);}publicclassFunctionalAnnotation{goodbyepublicStringgoodbye(Stringarg){returnGoodbye,arg!;}publicstaticvoidmain(String〔〕args){FunctionalAnnotationfanewFunctionalAnnotation();FunctionalAnnotation没有实现Functional接口,所以不能直接赋值Functionalfacfa;Incompatible?但可以通过Lambda将函数赋值给接口(类型需要匹配)Functionalffa::goodbye;FunctionalNoAnnfnafa::goodbye;FunctionalflaGoodbye,a;FunctionalNoAnnfnalaGoodbye,a;}}
  以上是自己创建函数式接口的示例。
  但在java。util。function包旨在创建一组完整的预定义接口,使得我们一般情况下不需再定义自己的接口。
  在java。util。function的函数式接口的基本使用基本准测,如下只处理对象而非基本类型,名称则为Function,Consumer,Predicate等,参数通过泛型添加如果接收的参数是基本类型,则由名称的第一部分表示,如LongConsumer,DoubleFunction,IntPredicate等如果返回值为基本类型,则用To表示,如ToLongFunction和IntToLongFunction如果返回值类型与参数类型一致,则是一个运算符如果接收两个参数且返回值为布尔值,则是一个谓词(Predicate)如果接收的两个参数类型不同,则名称中有一个Bi基本类型
  下面枚举了基于Lambda表达式的所有不同Function变体的示例:classFoo{}classBar{Foof;Bar(Foof){this。ff;}}classIBaz{inti;IBaz(inti){this。ii;}}classLBaz{longl;LBaz(longl){this。ll;}}classDBaz{doubled;DBaz(doubled){this。dd;}}publicclassFunctionVariants{根据不同参数获得对象的函数表达式staticFunctionFoo,Barf1fnewBar(f);staticIntFunctionIBazf2inewIBaz(i);staticLongFunctionLBazf3lnewLBaz(l);staticDoubleFunctionDBazf4dnewDBaz(d);根据对象类型参数,获得基本数据类型返回值的函数表达式staticToIntFunctionIBazf5ibib。i;staticToLongFunctionLBazf6lblb。l;staticToDoubleFunctionDBazf7dbdb。d;staticIntToLongFunctionf8ii;staticIntToDoubleFunctionf9ii;staticLongToIntFunctionf10l(int)l;staticLongToDoubleFunctionf11ll;staticDoubleToIntFunctionf12d(int)d;staticDoubleToLongFunctionf13d(long)d;publicstaticvoidmain(String〔〕args){applyusageexamplesBarbf1。apply(newFoo());IBazibf2。apply(11);LBazlbf3。apply(11);DBazdbf4。apply(11);applyAsusageexamplesintif5。applyAsInt(ib);longlf6。applyAsLong(lb);doubledf7。applyAsDouble(db);基本类型的相互转换longapplyAsLongf8。applyAsLong(12);doubleapplyAsDoublef9。applyAsDouble(12);intapplyAsIntf10。applyAsInt(12);doubleapplyAsDouble1f11。applyAsDouble(12);intapplyAsInt1f12。applyAsInt(13。0);longapplyAsLong1f13。applyAsLong(13。0);}}
  以下是用表格整理基本类型相关的函数式接口:
  函数式接口
  特征
  用途
  方法名
  FunctionT,R
  接受一个参数,返回一个结果
  将输入参数转换成输出结果,如数据转换或映射操作
  Rapply(Tt)
  IntFunction
  接受一个int参数,返回一个结果
  将int值转换成输出结果
  Rapply(intvalue)
  LongFunction
  接受一个long参数,返回一个结果
  将long值转换成输出结果
  Rapply(longvalue)
  DoubleFunction
  接受一个double参数,返回一个结果
  将double值转换成输出结果
  Rapply(doublevalue)
  ToIntFunction
  接受一个参数,返回一个int结果
  将输入参数转换成int输出结果
  intapplyAsInt(Tvalue)
  ToLongFunction
  接受一个参数,返回一个long结果
  将输入参数转换成long输出结果
  longapplyAsLong(Tvalue)
  ToDoubleFunction
  接受一个参数,返回一个double结果
  将输入参数转换成double输出结果
  doubleapplyAsDouble(Tvalue)
  IntToLongFunction
  接受一个int参数,返回一个long结果
  将int值转换成long输出结果
  longapplyAsLong(intvalue)
  IntToDoubleFunction
  接受一个int参数,返回一个double结果
  将int值转换成double输出结果
  doubleapplyAsDouble(intvalue)
  LongToIntFunction
  接受一个long参数,返回一个int结果
  将long值转换成int输出结果
  intapplyAsInt(longvalue)
  LongToDoubleFunction
  接受一个long参数,返回一个double结果
  将long值转换成double输出结果
  doubleapplyAsDouble(longvalue)
  DoubleToIntFunction
  接受一个double参数,返回一个int结果
  将double值转换成int输出结果
  intapplyAsInt(doublevalue)
  DoubleToLongFunction
  接受一个double参数,返回一个long结果
  将double值转换成long输出结果
  longapplyAsLong(doublevalue)非基本类型
  在使用函数接口时,名称无关紧要只要参数类型和返回类型相同。Java会将你的方法映射到接口方法。示例:importjava。util。function。BiConsumer;classIn1{}classIn2{}publicclassMethodConversion{staticvoidaccept(In1in1,In2in2){System。out。println(accept());}staticvoidsomeOtherName(In1in1,In2in2){System。out。println(someOtherName());}publicstaticvoidmain(String〔〕args){BiConsumerIn1,In2bic;bicMethodConversion::accept;bic。accept(newIn1(),newIn2());在使用函数接口时,名称无关紧要只要参数类型和返回类型相同。Java会将你的方法映射到接口方法。bicMethodConversion::someOtherName;bic。accept(newIn1(),newIn2());}}
  输出结果:accept()someOtherName()
  将方法引用应用于基于类的函数式接口(即那些不包含基本类型的函数式接口)importjava。util。Comparator;importjava。util。function。;classAA{}classBB{}classCC{}publicclassClassFunctionals{staticAAf1(){returnnewAA();}staticintf2(AAaa1,AAaa2){return1;}staticvoidf3(AAaa){}staticvoidf4(AAaa,BBbb){}staticCCf5(AAaa){returnnewCC();}staticCCf6(AAaa,BBbb){returnnewCC();}staticbooleanf7(AAaa){returntrue;}staticbooleanf8(AAaa,BBbb){returntrue;}staticAAf9(AAaa){returnnewAA();}staticAAf10(AAaa,AAbb){returnnewAA();}publicstaticvoidmain(String〔〕args){无参数,返回一个结果SuppliersClassFunctionals::f1;s。get();比较两个对象,用于排序和比较操作ComparatorcClassFunctionals::f2;c。compare(newAA(),newAA());执行操作,通常是副作用操作,不需要返回结果ConsumerconsClassFunctionals::f3;cons。accept(newAA());执行操作,通常是副作用操作,不需要返回结果,接受两个参数BiConsumerbiconsClassFunctionals::f4;bicons。accept(newAA(),newBB());将输入参数转换成输出结果,如数据转换或映射操作FunctionfClassFunctionals::f5;CCccf。apply(newAA());将两个输入参数转换成输出结果,如数据转换或映射操作BiFunctionbifClassFunctionals::f6;ccbif。apply(newAA(),newBB());接受一个参数,返回boolean值:测试参数是否满足特定条件PredicatepClassFunctionals::f7;booleanresultp。test(newAA());接受两个参数,返回boolean值,测试两个参数是否满足特定条件BiPredicatebipClassFunctionals::f8;resultbip。test(newAA(),newBB());接受一个参数,返回一个相同类型的结果,对输入执行单一操作并返回相同类型的结果,是Function的特殊情况UnaryOperatoruoClassFunctionals::f9;AAaauo。apply(newAA());接受两个相同类型的参数,返回一个相同类型的结果,将两个相同类型的值组合成一个新值,是BiFunction的特殊情况BinaryOperatorboClassFunctionals::f10;aabo。apply(newAA(),newAA());}}
  以下是用表格整理的非基本类型的函数式接口:
  函数式接口
  特征
  用途
  方法名
  Supplier
  无参数,返回一个结果
  获取值或实例,工厂模式,延迟计算
  Tget()
  Comparator
  接受两个参数,返回int值
  比较两个对象,用于排序和比较操作
  intcompare(To1,To2)
  Consumer
  接受一个参数,无返回值
  执行操作,通常是副作用操作,不需要返回结果
  voidaccept(Tt)
  BiConsumerT,U
  接受两个参数,无返回值
  执行操作,通常是副作用操作,不需要返回结果,接受两个参数
  voidaccept(Tt,Uu)
  FunctionT,R
  接受一个参数,返回一个结果
  将输入参数转换成输出结果,如数据转换或映射操作
  Rapply(Tt)
  BiFunctionT,U,R
  接受两个参数,返回一个结果
  将两个输入参数转换成输出结果,如数据转换或映射操作
  Rapply(Tt,Uu)
  Predicate
  接受一个参数,返回boolean值
  测试参数是否满足特定条件
  booleantest(Tt)
  BiPredicateT,U
  接受两个参数,返回boolean值
  测试两个参数是否满足特定条件
  booleantest(Tt,Uu)
  UnaryOperator
  接受一个参数,返回一个相同类型的结果
  对输入执行单一操作并返回相同类型的结果,是Function的特殊情况
  Tapply(Tt)
  BinaryOperator
  接受两个相同类型的参数,返回一个相同类型的结果
  将两个相同类型的值组合成一个新值,是BiFunction的特殊情况
  Tapply(Tt1,Tt2)多参数函数式接口
  java。util。functional中的接口是有限的,如果需要3个参数函数的接口怎么办?自己创建就可以了,如下:创建处理3个参数的函数式接口FunctionalInterfacepublicinterfaceTriFunctionT,U,V,R{Rapply(Tt,Uu,Vv);}
  验证如下:publicclassTriFunctionTest{staticintf(inti,longl,doubled){return99;}publicstaticvoidmain(String〔〕args){方法引用TriFunctionInteger,Long,Double,Integertf1TriFunctionTest::f;Lamdba表达式TriFunctionInteger,Long,Double,Integertf2(i,l,d)12;}}高阶函数
  高阶函数(HigherorderFunction)其实很好理解,并且在函数式编程中非常常见,它有以下特点:接收一个或多个函数作为参数返回一个函数作为结果
  先来看看一个函数如何返回一个函数:importjava。util。function。Function;interfaceFuncSSextendsFunctionString,String{}〔1〕使用继承,轻松创建属于自己的函数式接口publicclassProduceFunction{produce()是一个高阶函数:既函数的消费者,产生函数的函数staticFuncSSproduce(){returnss。toLowerCase();〔2〕使用Lambda表达式,可以轻松地在方法中创建和返回一个函数}publicstaticvoidmain(String〔〕args){FuncSSfuncSSproduce();System。out。println(funcSS。apply(YELLING));}}
  然后再看看,如何接收一个函数作为函数的参数:classOne{}classTwo{}publicclassConsumeFunction{staticTwoconsume(FunctionOne,Twoonetwo){returnonetwo。apply(newOne());}publicstaticvoidmain(String〔〕args){Twotwoconsume(onenewTwo());}}
  总之,高阶函数使代码更加简洁、灵活和可重用,常见于Stream流式编程中闭包
  在Java中,闭包通常与lambda表达式和匿名内部类相关。简单来说,闭包允许在一个函数内部访问和操作其外部作用域中的变量。在Java中的闭包实际上是一个特殊的对象,它封装了一个函数及其相关的环境。这意味着闭包不仅仅是一个函数,它还携带了一个执行上下文,其中包括外部作用域中的变量。这使得闭包在访问这些变量时可以在不同的执行上下文中保持它们的值。
  让我们通过一个例子来理解Java中的闭包:publicclassClosureExample{publicstaticvoidmain(String〔〕args){inta10;intb20;这是一个闭包,因为它捕获了外部作用域中的变量a和bIntBinaryOperatorclosure(x,y)xayb;intresultclosure。applyAsInt(3,4);System。out。println(Result:result);输出Result:110}}
  需要注意的是,在Java中,闭包捕获的外部变量必须是final或者是有效的final(即在实际使用过程中保持不变)。这是为了防止在多线程环境中引起不可预测的行为和数据不一致。函数组合
  函数组合(FunctionComposition)意为多个函数组合成新函数。它通常是函数式编程的基本组成部分。
  先看Function函数组合示例代码:importjava。util。function。Function;publicclassFunctionComposition{staticFunctionString,Stringf1s{System。out。println(s);returns。replace(A,);},f2ss。substring(3),f3ss。toLowerCase(),重点:使用函数组合将多个函数组合在一起compose是先执行参数中的函数,再执行调用者andThen是先执行调用者,再执行参数中的函数f4f1。compose(f2)。andThen(f3);publicstaticvoidmain(String〔〕args){Stringsf4。apply(GOAFTERALLAMBULANCES);System。out。println(s);}}
  代码示例使用了Function里的compose()和andThen(),它们的区别如下:compose是先执行参数中的函数,再执行调用者andThen是先执行调用者,再执行参数中的函数
  输出结果:AFTERALLAMBULANCESfterllmbulnces
  然后,再看一段Predicate的逻辑运算演示代码:publicclassPredicateComposition{staticPredicateStringp1ss。contains(bar),p2ss。length()5,p3ss。contains(foo),p4p1。negate()。and(p2)。or(p3);使用谓词组合将多个谓词组合在一起,negate是取反,and是与,or是或publicstaticvoidmain(String〔〕args){Stream。of(bar,foobar,foobaz,fongopuckey)。filter(p4)。forEach(System。out::println);}}
  p4通过函数组合生成一个复杂的谓词,最后应用在filter()中:negate():取反值,内容不包含barand(p2):长度小于5or(p3):或者包含f3
  输出结果:foobarfoobaz
  在java。util。function中常用的支持函数组合的方法,大致如下:
  函数式接口
  方法名
  描述
  FunctionT,R
  andThen
  用于从左到右组合两个函数,即:h(x)g(f(x))
  FunctionT,R
  compose
  用于从右到左组合两个函数,即:h(x)f(g(x))
  Consumer
  andThen
  用于从左到右组合两个消费者,按顺序执行两个消费者操作
  Predicate
  and
  用于组合两个谓词函数,返回一个新的谓词函数,满足两个谓词函数的条件
  Predicate
  or
  用于组合两个谓词函数,返回一个新的谓词函数,满足其中一个谓词函数的条件
  Predicate
  negate
  用于对谓词函数取反,返回一个新的谓词函数,满足相反的条件
  UnaryOperator
  andThen
  用于从左到右组合两个一元操作符,即:h(x)g(f(x))
  UnaryOperator
  compose
  用于从右到左组合两个一元操作符,即:h(x)f(g(x))
  BinaryOperator
  andThen
  用于从左到右组合两个二元操作符,即:h(x,y)g(f(x,y))柯里化
  柯里化(Currying)是函数式编程中的一种技术,它将一个接受多个参数的函数转换为一系列单参数函数。
  让我们通过一个简单的Java示例来理解柯里化:publicclassCurryingAndPartials{staticStringuncurried(Stringa,Stringb){returnab;}publicstaticvoidmain(String〔〕args){柯里化的函数,它是一个接受多参数的函数FunctionString,FunctionString,Stringsumabab;System。out。println(uncurried(Hi,Ho));通过链式调用逐个传递参数FunctionString,Stringhisum。apply(Hi);System。out。println(hi。apply(Ho));FunctionString,StringsumHisum。apply(Hup);System。out。println(sumHi。apply(Ho));System。out。println(sumHi。apply(Hey));}}
  输出结果:HiHoHiHoHupHoHupHey
  接下来我们添加层级来柯里化一个三参数函数:importjava。util。function。Function;publicclassCurry3Args{publicstaticvoidmain(String〔〕args){柯里化函数FunctionString,FunctionString,FunctionString,Stringsumabcabc;逐个传递参数FunctionString,FunctionString,Stringhisum。apply(Hi);FunctionString,Stringhohi。apply(Ho);System。out。println(ho。apply(Hup));}}
  输出结果:HiHoHup
  在处理基本类型的时候,注意选择合适的函数式接口:importjava。util。function。IntFunction;importjava。util。function。IntUnaryOperator;publicclassCurriedIntAdd{publicstaticvoidmain(String〔〕args){IntFunctionIntUnaryOperatorcurriedIntAddabab;IntUnaryOperatoradd4curriedIntAdd。apply(4);System。out。println(add4。applyAsInt(5));}}
  输出结果:9总结
  Lambda表达式和方法引用并没有将Java转换成函数式语言,而是提供了对函数式编程的支持(Java的历史包袱太重了),这些特性满足了很大一部分的、羡慕Clojure和Scala这类更函数化语言的Java程序员。阻止了他们投奔向那些语言(或者至少让他们在投奔之前做好准备)。总之,Lambdas和方法引用是Java8中的巨大改进

刘柏杰诗歌欣赏致余秀华致余秀华作者:刘柏杰笔下,我想写你,泪眼中看见一个摇晃的身影向我走来,在朗读者的舞台上读着你的大爱,《给你》在田间地头上,你以农家小女孩写着……高调秀恩爱!具俊晔将大S名字纹手臂上,脖子上还有甜蜜情话4月29日,中国台北一家刺青店曝光了大S和具俊晔的最新行踪,刺青店晒出了一组大S老公具俊晔纹身的照片,具俊晔纹在身上的每个图案都是在高调秀恩爱。刺青店在社交网络发文感谢受……希望这段话可以一直激励你如果此刻的你很迷茫,不知所措,请认真看完这篇文章,也许会对你有所启发。村上春树说,尽管眼下十分艰难,可日后这段经历说不定就会开花结果。当你实在熬不下去的时候,先停下……做一个小学生生活写尽人生苦,人生坎坷学做人,古国知海五千年,谦虚好学写一生,知识无穷勤为路,三人同行必有师,地大物博千文化,岁月年华写人生,年少学识尽白发,学识如海如水滴,人生品味悟人生,……清明到,你吃青团了吗?交汇点讯清明节最有代表的食物莫过于青团。清明前后,用艾草汁揉入糯米粉,包上或甜或咸的馅料做成碧绿色的团子,青团之名由此而来。如今,青团也成了网红美食,口味上不断创新,变得更丰富……魔灵召唤克罗尼柯战记全球上市享受结合策略与手操的乐趣《魔灵召唤》系列最新作《魔灵召唤:克罗尼柯战记》于全球170多个国家上市,目前玩家可于GooglePlayAppStoreSteam等多种平台享受游戏的乐趣。官方表示,《……直击时装周刘涛挽着米兰未尽的风奔赴巴黎明媚的春出席Chlo3月2日晚上9点(巴黎时间下午2点),她身着Chlo2023春季成衣系列长筒连衣裙,外搭黑色西装出席Chlo2023秋冬大秀。长款背心连衣裙由多层闪亮的小羊皮硬币制成,采……春游自驾租车订单攀升,热门车型预订拼手速每年春节过后,会进入一个旅游出行的淡季。而今年却与往年不同,随着天气渐暖,租车自驾去春游的需求增长明显,租车市场也跟着热起来。家住北京市朝阳区的华岩,一大早就来租赁公司取……从数字经济看职业教育转型化发展近几年以来,我国高度重视发展数字经济,并先后出台了《网络强国战略实施纲要》、《数字经济发展战略纲要》,从国家层面部署推动数字经济发展。这也标志着我国推动数字经济发展已经上升到了……痔疮犯了很难受?教你几招缓解不适症状红网时刻新闻4月6日讯(通讯员舒丽)俗话说十男九痔,十女十痔,虽然有夸张的成分,但也在一定程度上反映了痔疮的普遍性。痔疮虽不是什么大病,但是频繁发作,也会让人坐立难安,苦不堪言……欧文以一己之力毁了两支强队,甚至包含一支总冠军球队!欧文自从2011年进入NBA以来,一共经历了骑士、凯尔特人、篮网和独行侠四支球队。在骑士待的时间最长,一共待了六年,作为球队老二辅佐詹姆斯拿了一次总冠军,作为状元也算是在骑士成……达洛特想要赢更多我们必须要更加稳定下一场我们要做出回应直播吧4月3日讯英超第29轮,曼联客场02不敌纽卡。赛后曼联后卫达洛特接受了采访。谈到本场失利,达洛特表示:来形容这场失利的一个恰当的词是失望,来到客场,我们想按照计划来……
HiKeys,Fusion之后又出HiKeys,杜伽这次玩大前言,Fusion是我的心头好。大家好,我是林老师,我们又见面了。对于杜伽的品牌,我一直最喜欢的就是他们家的Fusion,唯一的不足就是不带背光,不过作为68键的经……熬最深的夜,月薪4000元,内容审核员为什么还没有被AI取代刘增(化名)想辞职了。最近两个月他的绩效在同组人员中一直都垫底。2019年我在阿里的外包公司工作,2020年我进入了腾讯的外包公司。在这两家公司工作期间,刘增的工作内容是……日本人祖宗是谁?DNA检测结果指向中国两省,民众看后难以接受地球上为何会有人类,我们又是如何发展的?尽管有科学家进行过研究,但我们始终半信半疑,毕竟在一定程度上,我们并不知道人类是如何演变的。在这样的背景下,有关科学家就开始研究人类的类……争议!刘建宏为男足抱不平只要成绩不好就不该开豪车吃海参?国足在大年初一以13大比分输给越南之后,就一直处于舆论的风口浪尖上。争议的焦点,除了国足一贯懒散的球场漫步之外,还增加了吴曦躲球和国足集体吃海参的事情。甚至,一度有媒体猜……原来人变老,鼻基底也会越来越凹有一类演员是演不出贫穷的。郑恩彩扮演的真安娜,出场就是精致大小姐本人。徐睿知饰演各类财阀、强势女王角色就像量身定制。这类看起来比较有【精致傲气】的大美女,很大……画出雪域高原的文明年轮来源:人民网人民日报海外版图为桑耶寺外景。新华社记者晋美多吉摄大江流日夜,慷慨歌未央。藏族和其他各民族的交流,贯穿雪域高原历史发展始终。时至今日,越来越多的考……唐艺昕出席活动,针织开衫搭配短裙,活泼可爱的小仙女文桔彤作为一个艺人而言,什么才是最重要的呢,演技,身材,颜值,或者是对时尚的把控呢?那么小编觉得最优雅的女神应该就是四个方面都能做到,所以小编觉得女神唐艺昕就是这样的女艺……建议经常烫染头发的人坚持涂抹护发精油,发质会越来越光泽健康现在很多女孩子都喜欢烫染漂染头发,经常烫染头发的人,大多数人的发质都会出现发尾分叉、头发容易从中间断裂造成断发、脱发的情况,更多的人还有发质毛糙没有光泽,不管做什么造型都不好看……太强悍,北京冬奥会真正的梦之队,两大项金牌全部被同一国家夺得在体育领域提到梦之队,很多国内观众都会想到中国的国球乒乓球队,中国跳水队和中国举重队,因为这几支队伍的实力在全世界确实是最顶级的存在,只要有中国队选手参赛,这些项目的金牌基本上……恐怖的百慕大三角百慕大三角提起百慕大三角可谓无人不知无人不晓那里已经成了神秘地带的代表沉船失踪的飞机数量大的惊人人们很困惑为什么救援者从未发现过遇难船只飞机的残骸呢随着科学的发展当今的人们已经……8位星光大道冠军,有的离婚,有的被淡忘,有的口碑跌落放眼如今华语乐坛,一大半歌手都是选秀出身,湖南卫视的超女快男捧红了李宇春、张杰,浙江卫视的好声音捧红了张碧晨、吉克隽逸,台湾超级星光大道走出了林宥嘉、萧敬腾,……战场即舞台!和平精英X洛天依在海岛开音舞party了《和平精英》x洛天依联动来啦!2月11日,二次元虚拟偶像洛天依正式降临海岛,当最火竞技冒险世界中出现国风顶流的舞台,接下来将为全体特种兵带来一场带感的音舞派对!登录《和平精英》……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网