当前位置: 首页 > 后端技术 > Java

《回炉重造》——Lambda表达式

时间:2023-04-01 14:41:36 Java

前言Lambda表达式(LambdaExpression),相信大家对Lambda一定不陌生,毕竟我们在数学中经常用到它,也就是λ。但是,我觉得数学中的Lambda和编程语言中的Lambda表达式没什么关系。如果有关系,就有Lambda这个词,哦!当然还有一个关系就是Lambda演算。λ演算(英语:lambdacalculus,λ-calculus)是一套从数理逻辑发展而来的形式系统,使用变量绑定和代换规则来研究函数如何抽象定义、函数如何应用和递归。它由数学家阿朗佐丘奇于1930年代首次发表。λ演算作为一种应用广泛的计算模型,可以明确定义什么是可计算函数,任何可计算函数都可以用这种形式表示和求值,并且可以模拟单带图灵机的计算过程。回到编程语言方面,其实不仅Java引入了这个Lambda表达式,其他的编程语言也引入了,比如C++、Javascript、Python等等。当然,本文回顾了Java中的Lambda表达式。作为初学者,以下对Lambda的理解肯定不够严谨,甚至可能存在错误。希望各位观众在评论区指出!为什么要学习这个Lambda表达式?Java8的新特性简化了代码的编写。工作中会用到,防止别人写的代码看不懂。每个人都向我学习。什么是Lambda表达式?Lambda表达式是一个匿名函数。换句话说,如果你有一个匿名函数,那么这个函数就是所谓的Lambda表达式。所谓匿名函数,顾名思义,就是没有函数名的函数。那么肯定有朋友会说,没有函数名,怎么调用这个函数呢?是的,这是一个很好的问题,先保持怀疑!在回答这个问题之前,先说另一个概念——“函数式编程”。什么是函数式编程?函数式编程是一种编程范式。此外,还有声明式编程和命令式编程,也是编程范式。好吧,这里有一个新的专业术语——“编程范式(ProgrammingParadigm)”。范例?相信正在看书的你,在学习数据库知识的时候,一定学过第一范式、第二范式、第三范式,现在又多了一个编程范式,这是什么鬼?百度百科是这样说的:编程范式,programmingparadigmorprogrammingparadigm(英文:Programmingparadigm),(paradigm的意思是模型,模型,paradigm的意思是模式,方法),是一种典型的编程风格,指的是从事软件工程的一种典型风格(可以与方法论进行比较)。如:函数式编程、过程式编程、面向对象编程、指令式编程等都是不同的编程范式。是不是太官方了,没关系,简单的理解,我觉得知道函数式编程是一种写代码的风格就OK了。我们需要注意的是,函数式编程中的“函数”这个词是一个数学函数,不是我们现在习惯理解的函数,也就是说,它是纯数学概念的函数,即映射,比如$y=f(x)$,参数$x$通过函数$f$映射到$y$。函数式编程与Lambda表达式有什么关系?可以说,函数式编程允许使用表达式来表示函数,而这个表达式就是Lambda表达式。在Java中,函数式编程是通过接口规范实现的。接口有这样一个特点:接口只有一个抽象方法。该接口标有@FunctionalInterface注释。具有此功能的接口称为“功能接口”。现在,回到开头,“我如何在没有函数名的情况下调用这个函数?”这就是功能接口的目的。接口中只有一个抽象方法,不用指定方法名就可以用Lambda表达。通过方法可以调用函数(方法),在不知道函数名的情况下也可以实现调用。就像想找人(方法)在某个房间(接口)做事。我的房间里只有一个人,所以除了这个人没有其他人可以做事。不需要指定谁来帮忙,直接喊:就靠你了!(这个比喻可能不太恰当,当它大概是这个意思哈哈哈)函数式接口Comparator我们可以看一下Comparator接口,它有@FunctionalInterface注解,所以可以确定它是一个函数式接口。@FunctionalInterfacepublicinterfaceComparator{intcompare(To1,To2);布尔等于(对象对象);默认Comparatorreversed(){returnCollections.reverseOrder(this);}defaultComparatorthenComparing(Comparatorother){对象。requireNonNull(其他);return(Comparator&Serializable)(c1,c2)->{intres=compare(c1,c2);返回(res!=0)?res:other.compare(c1,c2);};}...}有朋友应该说了,这个接口有那么多方法,怎么还是函数式接口呢?注意,我们可以看到一个看似抽象的equals方法,但是,因为equals是Object中的一个方法,所以这次对Object类的方法的重新声明会使该方法成为一个具体的方法。所以,不要误会我的意思,这里唯一的抽象方法是比较方法。那么可能有朋友要说了,接口里面能不能有具体的方法呢?是的,没错,在Java8中,具体的方法可以写在接口中。比如上面的reversed和thenComparing方法就是具体的方法。通用函数接口java.lang.Runnable@FunctionalInterfacepublicinterfaceRunnable{publicabstractvoidrun();}java.util.concurrent.Callable@FunctionalInterfacepublicinterfaceCallable{Vcall()throwsException;}java.lang.reflect.InvocationHandler@FunctionalInterfacepublicinterfaceInvocationHandler{publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable;}如何使用Lambda表达式?在Java8之前,我们使用Collections的排序方法,它需要一个比较器,就像这样。等等,忘了排序方法有一个比较器参数?没关系,代码贴给你:publicstaticvoidsort(Listlist,Comparatorc){list.sort(c);}最初的写法是像这样,因为Comparator是一个接口,不能直接实例化,所以需要一个类将这个接口实现为一个真正的比较器类,然后将这个Comparator实例对象作为sort方法的第二个参数传入,实现排序,如下:publicclassKeyComparatorimplementsComparator{@Overridepublicintcompare(Integerv1,Integerv2){返回v1-v2;}}Listkeys=Arrays.asList(9,3,5,10,2);Collections.sort(keys,newKeyComparator());System.out.println(按键);//[2,3,5,9,10]后来这种写法比较麻烦,所以我们用匿名内部类改写这种写法,就不需要再写一个类来实现这个接口,直接使用匿名内部类。它是这样写的:Listkeys=Arrays.asList(9,3,5,10,2);Collections.sort(keys,newComparator(){@Overridepublicintcompare(Integerv1,整数v2){返回v1-v2;}});System.out.println(按键);//[2,3,5,9,10]现在,匿名内部类比Lambda表达式更麻烦,所以我们使用LambdaRewrite:Listkeys=Arrays.asList(9,3,5,10,2);Collections.sort(keys,(Integerv1,Integerv2)->{returnv1-v2;});System.out.println(键);//[2,3,5,9,10],对吧?(Integerv1,Integerv2)->{returnv1-v2;}canbecalledwithoutafunctionname.其实这还不是最简单的。最简单的是:(v1,v2)->v1-v2。好奇什么时候可以这么写?现在告诉你!基本语法(参数类型参数名)->{方法体}基本上这样写是没有问题的。说说什么时候可以写的简单点。为了阅读方便,下面的“方法”指的是功能接口中的抽象方法。方法没有参数,所以可以直接写括号,然后是箭头,再写方括号,最后写方法体,即()->{方法体}方法有多个参数,那么多个参数分开逗号,参数类型可以省略,即(v1,v2,...)->{方法体}方法只有一个参数,那么括号可以去掉,直接写参数名,然后箭头,然后是方括号和方法体,即v->{方法体}方法体只有一条语句,不管有没有返回值,可以省略花括号、return关键字和分号的声明。情况一:无参数无返回值的普通方法是Runnable接口。@FunctionalInterfacepublicinterfaceRunnable{publicabstractvoidrun();}不使用Lambda(使用匿名内部类):newThread(newRunnable(){@Overridevoidrun(){System.out.println("线程开始运行“);}})。开始();使用Lambda://Writing1newThread(()->{System.out.println("Thethreadstartedrunning")}).start();//Writing2,A语句,然后省略花括号,return关键字和语句分号newThread(()->System.out.println("线程开始运行")).start();情况2:该方法没有参数并返回值示例:Callableinterface@FunctionalInterfacepublicinterfaceCallable{Vcall()throwsException;}不使用Lambda(使用匿名内部类):FutureTaskstringFutureTask=newFutureTask<>(newCallable(){@OverridepublicStringcall()throwsException{return"这是返回值";}});stringFutureTask.run();System.out.println(stringFutureTask.get());使用Lambda://一条语句,省略大括号,返回关键字和语句分号FutureTaskstringFutureTask=newFutureTask<>(()->"Hereisthereturnvalue");stringFutureTask.run();System.out.println(stringFutureTask.get());情况三:方法有一个参数,有一个返回值。随机在JDK中找了一个接口,如下:@FunctionalInterfaceinterfaceRecognizer{booleanrecognize(intc);}Lambda未使用(使用匿名内部类):privatefinalRecognizerA=newRecognizer(){@Overridepublicbooleanrecognize(intc){returnc=='a'||c=='A';}}使用Lambda:privatefinalRecognizerA=(c)->c=='a'||c=='A';//一个参数,括号可以省略privatefinalRecognizerA=c->c=='a'||c=='A';情况4:方法有多个参数,有返回值,直接使用Comparator。此示例不使用Lambda(使用匿名内部类):Listkeys=Arrays.asList(9,3,5,10,2);keys.sort(newComparator(){@Overridepublicintcompare(Integerv1,Integerv2){returnv1-v2;}});UsingLambda:Listkeys=Arrays.asList(9,3,5,10,2);//多个参数用逗号分隔,类型可以省略,语句、花括号、return关键字和语句分号是省略keys.sort((v1,v2)->v1-v2);System.out.println(按键);至此,相信大家对Lambda表达式有了基本的了解。一般来说:必须是函数式接口才能使用Lambda表达式语法:(参数类型参数名)->{方法体}如果方法的参数列表:如果没有参数,可以直接使用();如果有参数,可以省略(),直接写参数;如果有多个参数,()不能省略。()中的参数类型可以省略。如果方法体:只有一条语句,不管是否有返回值,都可以省略花括号、return关键字和语句分号。处理逻辑过于臃肿复杂,最好用具体的子类重写,保证可读性。最后一篇受限于本人水平,难免有错误和不足之处。如果你发现了什么,请指出!最后,感谢您阅读本文,感谢您认真对待我的努力,希望这篇博客对您有所帮助!你轻轻竖起大拇指,那会为我心中的世界增添一颗璀璨耀眼的星!