lambda 表达式
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。
* @author Alan
* @Email no008@foxmail.com
正文
第一节:lambda 表达式简介
MyNumber
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing1; /** * * @author Alan_Liu * * * 函数式接口: * 函数式接口是仅指定了 一个抽象方法的接口。 * * 只有当没有指定默认实现时,接口方法才是抽象方法。 * 即:没有指定默认实现的接口方法隐式地是抽象方法,所以没有必须要使用abstract修饰符。 * */ //函数式接口 其功能由getValue定义的 interface MyNumber { double getValue(); //隐式抽象方法 且也是唯一方法。 /** * lambda表达式不是独立执行的。而是构建了一个函数式接口定义的抽象方法的实现。 * 该函数式接口定义了它的目标类型。 * 结果:只有在定义了lambda表达式的目标类型的上下文中,才能使用该表达式。 * 当把一个lambda表达式赋给一个函数式接口引用时,就创建了这样的上下文。其他目标类型上下文包括变量初始化,return语句和方法参数等。 * 如: * MyNumber myNum; * myNum = () -> 123.45; * * 当目标类型上下文中出现lambda表达式时,会自动创建实现了 函数式接口的 一个类的实例。函数式接口声明的抽象方法的行为由lambda表达式定义。当通过目标 * 调用该方法是,就会执行lambda表达式,因此 lambda 表达式提供了一种将代码片段转换为对象的方法。 * 因此,lambda表达式成了getValue()方法的实现。 * System.out.println("A fixed value: " + myNum.getValue()); * */ }
LambdaDemo
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing1; /** * * @author Alan_Liu * * * lambda 表达式简介: * 理解lambda 表达式的java实现: * 一:lambda表达式自身 * 二:函数式接口 * * lambda 表达式本质上就是一个匿名(未命名)方法。这个方法不是独立执行的。而是用于实现由函数式接口定义的另一个方法。 * 因此,lambda表达式会导致产生一个匿名类。lambda表达式也常称为闭包。 * * 函数式接口是仅包含一个抽象方法的接口。一般来说,这个方法指明了接口的目标用途。因此,函数式接口通常表示单个动作。 * 例如:标准接口Runnable 是一个函数式接口,因为它只定义了一个方法run()。因此,run()定义了Runnable的动作。 * 函数式 接口定义了lambda表达式的目标类型。 * * * lambda表达式的基础知识 * lambda 表达式在java语言中引入了一个新的语法 元素和操作符。 这个操作符是 ->,即称之为 lambda操作符或者箭头操作符。 * () -> 123.45 * lambda表达式需要的所有参数 -> lambda体,即lambda表达式要执行的动作。 * 可以把 -> 表达成“成了” 或者“进入” * * java 定义了两种lambda体。 * 一种包含单独一个表达式 * 一种包含一个代码块 * * * * * */ class LambdaDemo { /** * @param args */ public static void main(String args[]) { MyNumber myNum; // declare an interface reference // Here, the lambda expression is simply a constant expression. // When it is assigned to myNum, a class instance is // constructed in which the lambda expression provides an override // of the getValue() method in MyNumber. /** * 这个 lambda 表达式没有参数,所以参数列表为空。它返回常量值为123.45. * */ myNum = () -> 123.45; /** * 因此这个表达式的作用类似于下面的方法: double MyMeth() { return 123.45; } 当然 lambda表达式定义的方法没有名称。 */ // Call getValue(), which is overridden by the previously assigned // lambda expression. System.out.println("A fixed value: " + myNum.getValue()); // Here, a more complex expression is used. /** * 这个lambda 表达式使用了 Math.random() 获取一个伪随机数,将其乘以100 然后返回结果。 * 这个lambda表达式也不需要参数 */ myNum = () -> Math.random() * 100; // These call the lambda expression in the previous line. System.out.println("A random value: " + myNum.getValue()); System.out.println("Another random value: " + myNum.getValue()); /** * (n) ->(n%2)==0 * 如果参数N的值是偶数,这个lambda 表达式会返回true,尽管可以显式指定参数的类型,例如本列中的n,但是通常不需要这么做。 * 因为很多时候,参数的类型是可以推断出来的。于命名方法 一样,lambda表达式可以指定需要用到的任意数量的参数。 */ /** * lambda表达式必须兼容功能接口定义的方法。因此,这不会工作: */ // A lambda expression must be compatible with the method // defined by the functional interface. Therefore, this won't work: /** * lambda 表达式必须 与其想要的实现的抽象方法兼容。 * 因此,下面该行代码必须注释掉。因为此行代码是非法的 * 因为String类型的值与double类型不兼容,而 getValue()的方法类型是double. */ // myNum = () -> "123.03"; // Error! } }
第二节:演示:如何使用带参数的lambda表达式
NumericTest
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing2; //Demonstrate a lambda expression that takes a parameter. //Another functional interface. interface NumericTest { boolean test(int n); }
LambdaDemo2
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing2; /** * * @author Alan_Liu * * 该实例只要演示:如何使用带参数的lambda表达式 * * */ class LambdaDemo2 { public static void main(String args[]) { // A lambda expression that tests if a number is even. /* * 特别要注意下面该行测试奇偶性的lambda表达式: * (n) -> (n % 2) == 0; * 注意,这里没有指定N的类型,相反 n的类型是从上下文推断出来的。本列中,其类型是 * 从NumericTest接口定义的test()方法的参数类型推断出来的。而该参数的类型是 int. * 在lambda表达式中,也可以显式指定参数的类型。 * 如:(int n) -> (n % 2) == 0; * 其中 n被显式指定为int类型。通常没有必要显式指定类型,但是在需要的时候是可以指定的。 * * 关于lambda 表达式的另外一个重要的地方: * 函数式接口引用可以用来执行任何与其兼容的lambda表达式。注意:程序中定义了两个不同的lambda表达式, * 他们都与函数式接口 NumericTest 的test() 方法兼容。 * 第一个 isEven,用于确定值是否是偶数, * 第二个 isNonNeg 用于检查值是否为非负值。 * 两种情况下都会测试参数n的值, * 因为每个lambda表达式都与 test()兼容,所以都可以通过NumericTest 引用执行。 */ NumericTest isEven = (n) -> (n % 2) == 0; if (isEven.test(10)) { System.out.println("10 is even"); } if (!isEven.test(9)) { System.out.println("9 is not even"); } // Now, use a lambda expression that tests if a number // is non-negative. NumericTest isNonNeg = (n) -> n >= 0; if (isNonNeg.test(1)) { System.out.println("1 is non-negative"); } if (!isNonNeg.test(-1)) { System.out.println("-1 is negative"); } } /* * ==================执行结果================================== * 10 is even * 9 is not even * 1 is non-negative * -1 is negative * */ }
第三节:lambda 表达式: 测试一个数字是否是另外一个数字的因子
NumericTest2
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing3; //Demonstrate a lambda expression that takes two parameters. /** * * @author Alan_Liu * * lambda 表达式: * 测试一个数字是否是另外一个数字的因子 * */ interface NumericTest2 { boolean test(int n, int d); }
LambdaDemo3
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing3; /** * * @author Alan_Liu * * lambda 表达式: * 测试一个数字是否是另外一个数字的因子 * */ class LambdaDemo3 { public static void main(String args[]) { // This lambda expression determines if one number is // a factor of another. NumericTest2 isFactor = (n, d) -> (n % d) == 0; if (isFactor.test(10, 2)) System.out.println("2 is a factor of 10"); if (!isFactor.test(10, 3)) System.out.println("3 is not a factor of 10"); } /** * =======执行结果============================== * 2 is a factor of 10 * 3 is not a factor of 10 * 在这个程序中,函数式接口NumericTest2 定义了test()方法 * boolean test(int n, int d); * 在这个版本里。test()方法指定了两个参数, 因此 与test()方法兼容的lambda表达式也必须指定两个参数, * 注意指定这种lambda表达式的方式: * (n, d) -> (n % d) == 0; * 两个参数n和d在参数列表中指定,并使用逗号分隔开。可以把这个例子推而广之。 * 每当需要一个以上的参数时,就在lambda操作符的左侧,使用一个带括号的参数列表指定参数,参数之间使用逗号分隔开。 * 对于lambda表达式 中多个参数,有一点十分重要:如果需要显示声明一个参数的类型, * 例如:下面的代码是合法的 : * (int n,int d) -> (n % d) == 0; * 但是下面的不合法: * (int n, d) -> (n % d) == 0; */ }
第四节:块 lambda 表达式
NumericFunc
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing4; //A block lambda that computes the factorial of an int value. /** * * @author Alan_Liu * * 块 lambda 表达式 * * */ interface NumericFunc { int func(int n); }
BlockLambdaDemo
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing4; /** * * @author Alan_Liu * * 表达式 lambda * 如:(n, d) -> (n % d) == 0; 的lambda 体包含单个表达式,被称之为 lambad表达式体。或者 表达式lambda。 * 在表达式中,操作符右侧的代码必须包含单独一个表达式。 * * 块 lambda 表达式 * lambda 表达式 其中 右侧的代码可以由一个代码块构成,其中可以包含多条语句。这种类型的lambda体被称之为块体。具有块体的lambda表达式有时候称之为 块lambda。 * 块lambda表达式内部可以处理的操作类型,因为它允许lambda体包含多条 语句, * 例如:在块lambda中,可以声明变量,是用循环,指定if和swith语句,创建嵌入式代码块等。 * 创建lambda很容易,只需要使用花括号包围lambda体,就像创建其他语句块一样。 * * 块lambda中必须显示使用return语句来返回值。 * */ class BlockLambdaDemo { public static void main(String args[]) { // This block lambda computes the factorial of an int value. NumericFunc factorial = (n) -> { /** * 在 该块lambda 中声明了变量 result,使用了一个for循环,并且具有一条return语句。 * 在块lambda 体内,这么做事合法的。块lambda体在本质上 与方法体类似。 * 另外一点,当lambda 表达式中出现return 语句时,只是从lambda体返回,而不会导致包围lambda体的方法返回。 * */ int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; }; System.out.println("The factoral of 3 is " + factorial.func(3)); System.out.println("The factoral of 5 is " + factorial.func(5)); } /** * * =========执行结果============================ * The factoral of 3 is 6 * The factoral of 5 is 120 * * * */ }
第五节:块lambda 表达式 示例
StringFunc
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing5; //A block lambda that reverses the characters in a string. interface StringFunc { String func(String n); }
BlockLambdaDemo2
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing5; /** * * @author Alan_Liu * * 块lambda 表达式 示例: * 颠倒一个字符串中的字符 * */ class BlockLambdaDemo2 { public static void main(String args[]) { // This block lambda that reverses the charactrers in a string. StringFunc reverse = (str) -> { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; }; System.out.println("Lambda reversed is " + reverse.func("Lambda")); System.out.println("Expression reversed is " + reverse.func("Expression")); } /** * ======执行结果============== * Lambda reversed is adbmaL * Expression reversed is noisserpxE * */ }
第六节:泛型函数式接口
SomeFunc
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing6; //Use a generic functional interface with lambda expressions. //A generic functional interface. /** * * @author Alan_Liu * * @param <T> * * 泛型函数式接口: * lambda 表达式自身不能指定类型参数。 * 因此,lambda表达式不能是泛型(当然,由于存在类型推断,所有的lambda表达式都展现出一些类似于泛型的特征。) * 然而 与lambda表达式关联的函数式接口可以是泛型。 * 此时,lambda表达式的目标类型部分由声明函数式接口引用时指定参数类型决定。 * 为了理解函数式接口的值,考虑这样的情况,前一节的两个示例使用两个不同的函数接口, * 一个叫做NumericFunc 另外一个叫做StringFunc 。 * 但是,两个接口都定义了一个叫做func()方法,该方法接受一个参数,返回一个结果。 * 对于第一个接口,func()方法的参数类型和返回类型为int。 * 对与第二个 接口,func() 方法参数类型和返回类型是String。因此,两个方法的唯一区别是他们需要的数据类型。 * 相较于使用两个函数式接口,他们的方法只是在需要的数据类型方面存在区别,也可以只声明一个泛型接口来处理两种情况。 * 下面演示了这种方法: * */ interface SomeFunc<T> { T func(T t); }
GenericFunctionalInterfaceDemo
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing6; /** * * @author Alan_Liu * * @param <T> * * 泛型函数式接口: * lambda 表达式自身不能指定类型参数。 * 因此,lambda表达式不能是泛型(当然,由于存在类型推断,所有的lambda表达式都展现出一些类似于泛型的特征。) * 然而 与lambda表达式关联的函数式接口可以是泛型。 * 此时,lambda表达式的目标类型部分由声明函数式接口引用时指定参数类型决定。 * 为了理解函数式接口的值,考虑这样的情况,前一节的两个示例使用两个不同的函数接口, * 一个叫做NumericFunc 另外一个叫做StringFunc 。 * 但是,两个接口都定义了一个叫做func()方法,该方法接受一个参数,返回一个结果。 * 对于第一个接口,func()方法的参数类型和返回类型为int。 * 对与第二个 接口,func() 方法参数类型和返回类型是String。因此,两个方法的唯一区别是他们需要的数据类型。 * 相较于使用两个函数式接口,他们的方法只是在需要的数据类型方面存在区别,也可以只声明一个泛型接口来处理两种情况。 * 下面演示了这种方法: * */ class GenericFunctionalInterfaceDemo { public static void main(String args[]) { // Use a String-based version of SomeFunc. SomeFunc<String> reverse = (str) -> { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; }; System.out.println("Lambda reversed is " + reverse.func("Lambda")); System.out.println("Expression reversed is " + reverse.func("Expression")); /* *=====执行结果========================== * Lambda reversed is adbmaL * Expression reversed is noisserpxE */ // Now, use an Integer-based version of SomeFunc. SomeFunc<Integer> factorial = (n) -> { int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; }; System.out.println("The factoral of 3 is " + factorial.func(3)); System.out.println("The factoral of 5 is " + factorial.func(5)); /* *=====执行结果========================== * The factoral of 3 is 6 * The factoral of 5 is 120 */ } }
第七节:作为参数传递lambda 表达式
StringFunc
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing7; //Use lambda expressions as an argument to a method. /** * * @author Alan_Liu * * 作为参数传递lambda 表达式: * lambda表达式可以使用在任何提供了目标类型的上下文中。一种情况就是作为参数表达式lambda表达式。 * 事实上,这是lambda表达式的 一种常见用途。另外,这也是lambda表达式的一种强大 用途。 * 因为可以将可执行代码作为参数传递给方法。这极大的强化了java的表达式。 * 为将lambda表达式作为参数传递,接受lambda表达式的参数的类型必须是与该lambda表达式兼容的函数接口的类型。 * 虽然使用lambda表达式作为参数十分直观,但是使用例子进行 演示仍然会有帮助。 * * */ interface StringFunc { String func(String n); }
LambdasAsArgumentsDemo
package com.alanliu.Java8BasicCodeStuding.Java8BasciCode.Unit15_Lambda.listing7; /** * * @author Alan_Liu * * 作为参数传递lambda 表达式: * lambda表达式可以使用在任何提供了目标类型的上下文中。一种情况就是作为参数表达式lambda表达式。 * 事实上,这是lambda表达式的 一种常见用途。另外,这也是lambda表达式的一种强大 用途。 * 因为可以将可执行代码作为参数传递给方法。这极大的强化了java的表达式。 * 为将lambda表达式作为参数传递,接受lambda表达式的参数的类型必须是与该lambda表达式兼容的函数接口的类型。 * 虽然使用lambda表达式作为参数十分直观,但是使用例子进行 演示仍然会有帮助。 * * */ class LambdasAsArgumentsDemo { // This method has a functional interface as the type of // its first parameter. Thus, it can be passed a reference to // any instance of that interface, including the instance created // by a lambda expression. // The second parameter specifies the string to operate on. /* * 在该程序中,首先需要注意stringOp()方法,它有2个参数, * 第一个参数的类型是 StringFunc , 而 StringFunc 是一个函数式接口。因此,这个参数可以接受对任何StringFunc实例的引用, 包括由lambda表达式创建的实例。 * 第二个参数是String 类型,也就是要操作的字符串。 */ static String stringOp(StringFunc sf, String s) { return sf.func(s); } public static void main(String args[]) { String inStr = "Lambdas add power to Java"; String outStr; System.out.println("Here is input string: " + inStr); /* * =========执行结果:===================== * Here is input string: Lambdas add power to Java */ // Here, a simple expression lambda that uppercases a string // is passed to stringOp( ). /* * 接下来:注意对stringOp() 的第一次调用,如代码所示: * */ outStr = stringOp((str) -> str.toUpperCase(), inStr); System.out.println("The string in uppercase: " + outStr); /*这里传递了一个简单的表达式lambda作为参数,这会参建函数式接口StringFunc的 一个实例,并作为对该实例的 一个 * 引用传递给stringOp()方法的第一个参数。这就把嵌入在一个类实例中的lambda表达式传递给了方法。 * 目标类型上下文由参数的类型决定。因此lambda表达式与该类型兼容,调用时合法的。 * 在方法调用内嵌入表达式只使用一次时。 * * =========执行结果:===================== * The string in uppercase: LAMBDAS ADD POWER TO JAVA */ // This passes a block lambda that removes spaces. /** * 接下来:程序向StringOp()传递了一个块lambda。这个lambda表达式删除字符串中的空格。 * 如下所示: */ outStr = stringOp((str) -> { String result = ""; int i; for (i = 0; i < str.length(); i++) { if (str.charAt(i) != ' ') { result += str.charAt(i); } } return result; }, inStr); System.out.println("The string with spaces removed: " + outStr); /* * 虽然这里使用了一个块lambda,但是传递过程与刚才讨论的传递简单的表达式lambda相同。但是,在本例子中, * 一些程序员可以发现语法有些笨拙。 * 当块lambda看上去特别长,不适合嵌入到方法调用中时,很容易把块lambda赋给一个函数式接口变量,正如前面的几个例子 * 中所做的那样。然后,可以简单地把该引用传递给方法。 * =========执行结果:===================== * The string with spaces removed: LambdasaddpowertoJava */ // Of course, it is also possible to pass a StringFunc instance // created by an earlier lambda expression. For example, // after this declaration executes, reverse refers to a // synthetic instance of StringFunc. /** *该部分演示的内容如下: * 先定义一个颠倒字符串的块lambda,然后把该块lambda赋给reverse.reverse是一个对StringFunc实例的引用。 * 因此,并传入reverse和要操作的字符串。 * 因为计算每个lambda表达式得到的实例是StringFunc实现。所以可以用作StringOp()方法的第一个参数。 * */ StringFunc reverse = (str) -> { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; }; // Now, reverse can be passed as the first parameter to stringOp() // since it refers to a StringFunc object. System.out.println("The string reversed: " + stringOp(reverse, inStr)); /* * =========执行结果:===================== * The string reversed: avaJ ot rewop dda sadbmaL */ } }
源码下载
为人:谦逊、激情、博学、审问、慎思、明辨、 笃行
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?