【Java】3.0 Java中lambda表达式(上)
2019-07-11 本文已影响5人
bobokaka
1.0 lambda表达式是JDK8新增的功能,它显著增强了Java。
自C#和C++等语言都添加了lambda表达式后,Java8也开始不甘人后,继续保持自身的活力和创造性。lambda表达式正在重塑Java,将影响到几乎所有的Java程序员。
2.0 lambda表达式本质是一个匿名(即未命名)方法。实现和理解其实很简单。
- lambda表达式在Java语言中引入一个新的语法元素和操作符,操作符是“
->
”,可以称它为lambda操作符或者箭头操作符。符号左侧是参数(没有就打空括号),右侧是具体执行的动作。用自然语言描述时,可以把“->
”表达成“成了
”或者“进入
”。 - 举个例子,
() -> 123.45
上面的内容,效果等同于如下所示:
double myMeth(){
return 123.45;
}
第二个代码中小括号内的内容就是第一个代码小括号中的内容,有参数或者无参数。
- 再举个例子。
(n) -> (n%2) == 0
上面的内容,效果等同于如下所示:
boolean myExample(int n){
return (n%2) == 0;
}
3.0 高级的来了: 单个表达式
3.1 无参数的单个lambda表达式。先看完整代码。
package com.edpeng.game1;
interface MyNumber {
// 从JDK8开始,可以为接口声明默认行为,即所谓的“默认方法”
double getValue();
}
public class test1 {
public static void main(String[] args) {
MyNumber myNum;
// 第一个例子
myNum = () -> 123.45;
System.out.println("第一个例子输出结果为:" + myNum.getValue());
// 第二个例子,Math.random(),生成0-1之间的伪随机数
myNum = () -> Math.random() * 100;
System.out.println("第二个例子第一个值输出结果为:" + myNum.getValue());
System.out.println("第二个例子第二个值输出结果为:" + myNum.getValue());
// myNum = () -> "123.45";//会报错!无法编译运行。
}
}
执行结果如下:
图片.png
- (1)本例中,
getValue()
方法属于隐式抽象方法,并且是MyNumber
定义的唯一方法。因此,MyNumber
是一个函数式接口,其功能由getValue()
定义。而我们的 lambda表达式定义了函数式接口声明的抽象方法的行为。 - (2)需要特别说明的是, lambda表达式不能单独执行(单独出现),是要依赖上下文的。意思就是说,目标类型上下文应该能够明确 lambda表达式所需要的变量初始化,return语句和方法参数等。当把一个 lambda表达式赋给一个函数式接口引用时,就创建了这样的上下文。
- (3)对上面
(2)
的进一步补充和阐述。例如如果抽象方法指定了两个int型参数,那么 lambda表达式也必须制定两个参数,其类型要么显性指定为int型,要么在上下文中可以被隐式推断为int类型。总之,lambda表达式的参数的类型和数量必须与方法的参数兼容,返回类型也必须兼容,并且 lambda表达式可能抛出的异常也必须能被方法接受。
3.2 有参数的单个lambda表达式。下面是有参表达式的完整代码实例:
package com.edpeng.game1;
interface NumericTest {
// 从JDK8开始,可以为接口声明默认行为,即所谓的“默认方法”
boolean test(int n);
}
public class test1 {
public static void main(String[] args) {
// 例子1
NumericTest isEven = (n) -> (n % 2) == 0;
int number = 10;
if (isEven.test(number)) {
System.out.println(number + " 能被2整除。");
} else {
System.out.println(number + " 不能被2整除。");
}
// 例子2
NumericTest isNonNeg = (n) -> n >= 0;
number = 1;
if (isNonNeg.test(number)) {
System.out.println(number + " 是自然数。");
} else {
System.out.println(number + "不是自然数。");
}
number = -1;
if (isNonNeg.test(number)) {
System.out.println(number + "是自然数。");
} else {
System.out.println(number + "不是自然数。");
}
}
}
运行结果如下:
图片.png
4.0 块lambda表达式
具体格式如下:
() -> { };
4.1 看代码就懂了,无非就是箭头符号后面不止一句语句,可以用花括号整合在一起执行。
package com.edpeng.game1;
interface Numericfunc {
int func(int n);
}
public class test1 {
public static void main(String[] args) {
Numericfunc numericfunc = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++) {
result = i * result;
}
return result;
};
// 3的阶乘:1*2*3
System.out.println("3的阶乘是:" + numericfunc.func(3));
// 5的阶乘:1*2*3*4*5
System.out.println("5的阶乘是:" + numericfunc.func(5));
}
}
执行结果为:
图片.png
- lambda表达式的意思是:
需要给我一个参数n,并需要提供最后返回的值,而且都要和抽象函数对象匹配,通过这个函数可以推出需要的参数n为int类型,并且返回值也应该为int类型
。(由int func(int n)
推出)。 - 需要注意的是,代码中两个n是两回事,lambda表达式中的n是自己代码块里面用,你们可以换成任何可以自由定义的别的单词,且lambda表达式不能单独声明参数类型,指定数据类型,因为可以由上下文推出。
4.2 在上面4.1中都是int型,下面可以举一个别的类型的实现。
package com.edpeng.game1;
interface Stringfunc {
String func(String n);
}
public class test1 {
public static void main(String[] args) {
Stringfunc stringfunc = (str) -> {
String result = "";
int i;
for (i = str.length() - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
};
System.out.println("将lambda字母颠倒输出为:\n" + stringfunc.func("lambda"));
System.out.println("将Expression字母颠倒输出为: \n" + stringfunc.func("Expression"));
}
}
执行结果为:
图片.png
5.0 泛型函数式接口。
5.1 上面4.2中已经阐述过了,这里需要再强调一下,lambda表达式自身不能指定类型参数。也因此,lambda表达式不能是泛型(当然,它本身存在类型推断,自带一些类似泛型的特征)。
5.2 但是,,lambda表达式关联的函数式接口可以是泛型。这时候,lambda表达式的目标类型由声明函数式接口引用时指定的参数类型决定。
5.3 基于此,我们可以把4.1和4.2中两个范例通过泛型函数式接口整合在一起。代码如下:
package com.edpeng.game1;
//这就叫做“泛型函数式接口”
interface SomeFunc<T> {
T func(T t);
}
public class test1 {
public static void main(String[] args) {
// 4.1中的案例
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字母颠倒输出为:\n"
+ reverse.func("lambda"));
System.out.println("将Expression字母颠倒输出为: \n"
+ reverse.func("Expression"));
// 4.1中的案例等同于:
// SomeFunc<String> re = new SomeFunc<String>() {
//
// @Override
// public String func(String t) {
// // TODO Auto-generated method stub
// String result = "";
// int i;
// for (i = t.length() - 1; i >= 0; i--) {
// result += t.charAt(i);
// }
// return result;
// }
// };
// System.out.println("将lambda字母颠倒输出为:\n" +re.func("lambda"));
// 4.2中的案例
SomeFunc<Integer> factorial = (num) -> {
int result = 1;
for (int i = 1; i <= num; i++) {
result = i * result;
}
return result;
};
// 3的阶乘:1*2*3
System.out.println("3的阶乘是:" + factorial.func(3));
// 5的阶乘:1*2*3*4*5
System.out.println("5的阶乘是:" + factorial.func(5));
}
}
执行结果为:
图片.png
6.0 作为参数传递lambda表达式
这个方面是lambda表达式的一种常见用途,极大的增强了Java的表达能力。
通过一个简单的例子演示就足够了,特别简单直观好理解,而且实用。
package com.edpeng.game1;
interface StringFunc {
String func(String n);
}
class test1 {
static String stringOp(StringFunc sf, String string) {
return sf.func(string);
}
public static void main(String[] args) {
//案例一:小学转大写
String inStr = "lambda 表达式 加入 Java";
String outStr;
System.out.println("输入的字符串为: \n" + inStr);
outStr = stringOp((str) -> str.toUpperCase(), inStr);
System.out.println("该字符串的大写为: \n" + outStr);
//案例二:去除文本空格
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("删除空格的字符串为: \n" + outStr);
//案例三:字符倒转
StringFunc reverse = (str) -> {
String result = "";
int i;
for (i = str.length() - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
};
System.out.println("重新转换的字符串为: \n" + stringOp(reverse, inStr));
}
}
执行结果如下:
图片.png
这个并没有增加新的内容,只是将其中增加的这个特性再一次强调一下。案例一和案例二都可以简单表述为:
outStr = stringOp(lambda表达式,inStr);
当然我们也看当这样使用时有些笨拙的,当块lambda表达式看上去特别长的时候,嵌入到方法调用中,很容易把块lambda赋给一个函数式接口变量。所以当使用案例三这种做法时,就显得很简单而且不易出错。案例三可以简单描述为:
StringFunc reverse =lambda表达式;
stringOp(reverse, inStr);