Lambda表达式的含义
Lambda省去面向对象的条条框框,格式由3个部分组成:
- 一些参数
- 一个箭头
- 一段代码
Lambda表达式的标准格式为:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
( )
:接口中的抽象方法的参数列表,无参数则留空,有参数就写出参数;多个参数则用逗号分隔。->
:是新引入的语法格式,代表指向动作,传递的意思,把小括号内的参数传递给后面的方法体{ }。{ }
:语法与传统方法体要求基本一致即重写接口的抽象方法的方法体。
Lambda省略格式
可推导即可省略
Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。例如上例还可以使用Lambda的省略写法:
public static void main(String[] args) {
invokeCalc(120, 130, (a, b) -> a + b);
}
Lambda省略规则
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参数,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
注意:要省略 {}
,return
, ;
必须一起省略
Lambda表达式与函数式接口
Lambda表达式的类型,也被称为“目标类型(target type)" ,Lambda表达式的目标类型必须是”函数式接口(function interface)"。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法(default修饰),类方法,但只能声明一个抽象方法。
由于Lambda表达式的结果就是被当成对象,因此程序中完全可以使用Lambda表达式进行赋值,例如如下代码。
// Runnable接口只包含一个无参数的方法
// Lambda表达式代表的匿名方法实现了Runnable接口中唯一的,无参数的方法
// 因此下面的Lambda表达式创建了一个Runnable对象
Runnable r = () -> {
for (int i = 0; i < 100; i ++) {
System.out.println();
}
};
从上面的代码可以看出,Lambda表达式实现的是匿名方法-因此它只能实现特定函数式接口中的唯一方法。这意味着Lambda表达式有如下两个限制:
- Lambda表达式的目标类型必须是明确的函数式接口
- Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口(函数式接口)创建对象。
重点:
为保证Lambda表示式的目标类型是一个明确的函数式接口,可以有如下三种常见方式。
- 将Lambda表示式赋给函数式接口类型的变量
- 将Lambda表示式作为函数式接口类型的参数传给某个方法
- 使用函数式接口对Lambda表示式进行强制类型转换
//错误写法:会报不兼容的类型 Object 不是函数式接口
Object obj = () -> {
for (int i = 0; i < 100; i++) {
System.out.println();
}
};
// 正确写法
Object obj = (Runnable) () -> {
for (int i = 0; i < 100; i++) {
System.out.println();
}
};
Java 8在java.util.function包下预定义了大量函数式接口,典型地包含如下4类接口.
XxxFunction:这类接口中通常包含一个apply()抽象方法,该方法对掺数进行处理、转换(apply()
方法的处理逻辑由Lambda表达式来实现),然后返回一个新的值。该函数式接口通常用于对指定数据进行转换处理.
XxxConsumer:这类接口中通常包含一个accept()抽象方法,该方法与XxxFunction接口中的apply()方法基本相似,也负责对參数进行处理,只是该方法
不会返回
处理结果.
XxxxPredicate,这类接口中通常包含一个test()抽象方法,该方法通常用来对参数进行某种判断
(test()方法的判断逻辑由Lambda表达式来实现),然后返回一个boolean值.该接口通常用于判断参数是否满足特定条件,经常用于进行筛滤数据.
XxxSupplier:这类接口中通常包含一个getAsXxx()抽象方法,该方法不需要输入参数,该方法会按照某种逻辑算法(getAsXxx()方法的逻辑算法由Lambda表达式实现)返回一个数据。
方法引用与构造器引用
如果Lambda表达式的代码块只有一条代码,程序就可以省略Lambda表达式中代码块的花括号。不仅如此,如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。
方法引用和构造器引用可以让Lambda表达式的代码块更加简洁。方法引用和构造器引用都需要使用两个英文冒号。
Lambda表达式支持如所示的几种引用方式。
种类 | 示例 | 说明 | 对应的Lambda表达式 |
---|---|---|---|
引用类方法 | 类名::类方法 | 函数式接口中被实现方法的全部参数传给该类方法作为参数 | (a,b…) -> 类名.类方法(a,b…) |
引用特定对象的实例方法 | 特定对象::实例方法 | 函数式接口中被实现方法的全部参数传给该方法作为参数 | (a,b,…) -> 特定对象.实例方法(a,b,…) |
引用某类对象的实例方法 | 类名::实例方法 | 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 | (a,b,…) -> a.实例方法(b,…) |
引用构造器 | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | (a,b,…) -> new 类名(a,b,…) |
举例说明
@FunctionInterface
interface Converter {
Integer convert(String from);
}
- 引用方法
Lambda实现:
Converter converter1 = from -> Integer.valueOf(from);
方法引用实现:
Converter converter1 = Integer::valueOf;
Integer val = converter1.convert("99");
System.out.println(val); // 99
2.引用特定对象的实例方法
Lambda实现:
Converter converter2 = from -> "helloworld".indexOf(from);
方法引用实现:
Converter converter2 = "helloworld"::indexOf;
Integer value = Converter2.convert("it");
System.out.println(value); // 2
3.引用某类对象的实例方法
@FunctionInterface
interface MyTest {
String test(String a, int b, int c);
}
Lambda实现:
MyTest mt = (a, b, c) -> a.subString(b, c);
方法引用实现:
Mytest mt = String::subString;
String str = mt.test("Java I Love you", 2, 9);
System.out.println(str); // va I Lo
- 引用构造器
@FunctionInterface
interface YourTest {
JFrame win(String title);
}
Lambda实现:
YourTest yt = (String a) -> new JFrame(a);
方法引用实现:
YourTest yt = JFrame::new;
JFrame jf = yt.win("我的窗口");
System.out.println(jf);
综合对比
package java_code_exercise;
// _ooOoo_
// o8888888o
// 88" . "88
// (| -_- |)
// O\ = /O
// ____/`---'\____
// . ' \\| |// `.
// / \\||| : |||// \
// / _||||| -:- |||||- \
// | | \\\ - /// | |
// | \_| ''\---/'' | |
// \ .-\__ `-` ___/-. /
// ___`. .' /--.--\ `. . __
// ."" '< `.___\_<|>_/___.' >'"".
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
// \ \ `-. \_ __\ /__ _/ .-` / /
// ======`-.____`-.___\_____/___.-`____.-'======
// `=---='
//
// .............................................
// 佛祖保佑 永无BUG
// 佛曰:
// 写字楼里写字间,写字间里程序员;
// 程序人员写程序,又拿程序换酒钱。
// 酒醒只在网上坐,酒醉还来网下眠;
// 酒醉酒醒日复日,网上网下年复年。
// 但愿老死电脑间,不愿鞠躬老板前;
// 奔驰宝马贵者趣,公交自行程序员。
// 别人笑我忒疯癫,我笑自己命太贱;
// 不见满街漂亮妹,哪个归得程序员?
/**
@FunctionalInterface
public interface Converter {
Integer convert(String from);
}
*/
/**
* @author swordsmanye
* @data 2019/9/15 11:04
*/
public class LambdaStream {
public static void main(String[] args) {
// 匿名内部类方法实现
Converter converter = new Converter() {
@Override
public Integer convert(String from) {
return Integer.valueOf(from);
}
};
Integer val = converter.convert("98");
System.out.println(val);
// Lambda表达式实现
Converter converter1 = a -> Integer.valueOf(a);
Integer val1 = converter1.convert("99");
System.out.println(val);
// 方法引用
Converter converter2 = Integer::valueOf;
Integer val2 = converter2.convert("100");
System.out.println(val2);
}
}