Lambda表达式初探
Lambda表达式是java8的重要更新,是被广大开发者期待已久的新特性。使用Lambda表达式可以用更简洁的代码来创建函数式接口的实例。
lambda表达式的类型,也被称为目标类型,Lambda表达式的目标类型必须是函数式接口。函数式接口代表只包含一个抽象方法的接口,函数式接口可以包含多个默认方法、类方法、但只能声明一个抽象方法。使用@FunctionalInterface注解可以在编译期间检查该接口是否是函数式接口。
1.Lambda表达式入门
在没有Lambda表达式之前,我们可能会用这样的方式去实现一个接口:
interface Add {
int add(int a, int b);
}
public class LambdaDemo {
public static void main(String[] args) {
Add add = new Add() {
@Override
public int add(int a, int b) {
return a + b;
}
};
int result = add.add(3, 5);
System.out.println(result);
}
}
这样虽然也能达到目的,但是无形中就增加了代码量,也使得代码整体繁琐,难以阅读。但是使用Lambda就可以这样写:
@FunctionalInterface
interface Add {
int add(int a, int b);
}
public class LambdaDemo {
public static void main(String[] args) {
Add add = (a, b) -> a + b;
int result = add.add(3, 5);
System.out.println(result);
}
}
两者的结果是相同的,但是使用Lambda表达式实现接口时不需要new Xxx()这种繁琐的代码,不需要指出重写的方法名字,也不需要写出方法的返回类型。不仅大大减少了代码量,同时也使得代码清晰简洁,方便阅读。
从上面的例子中可以看出,当使用Lambda表达式替代匿名内部类创建对象时,Lambda表达式的代码块将会代替实现抽象方法的方法体,其表达式本身就相当于一个匿名方法,替代了匿名内部类的繁琐语法。
Lambda表达式由三部分组成:
- 形参列表:形参列表允许省略形参类型,如果形参列表中只有一个参数,圆括号也可以省略
- 箭头(->):英文画线号和大于符号
- 代码块:
- 如果代码块只包含一条语句,可以省略代码块的花括号
- 如果只有一条return语句,可以省略return关键字
- 如果需要返回值且代码块中只有一条省略了return的语句,Lambda表达式会自动返回这条语句的值
2.方法引用与构造器引用:
使用方法引用和构造器引用可以使代码更加简化:
public class LambdaDemo2 {
public static void main(String[] args) {
// 1.引用类方法
// 类名::类方法
// 函数式接口中的被实现方法的全部参数传给该类方法作为参数
Converter converter1 = value -> Integer.valueOf(value);
System.out.println(converter1.valueOf("1"));
Converter converter2 = Integer::valueOf;
System.out.println(converter2.valueOf("1"));
System.out.println("************************");
// 2.引用特定对象的实例方法
// 特定对象::实例方法
// 函数式接口中的被实现方法的全部参数传给该类方法作为参数
Converter converter3 = from -> "cnsuning.com".indexOf(from);
System.out.println(converter3.valueOf("i"));
Converter converter4 = "cnsuning.com"::indexOf;
System.out.println(converter4.valueOf("i"));
System.out.println("************************");
// 3.引用某类对象实例方法
// 类名::实例方法
// 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
MyTest test1 = (a, b, c) -> a.substring(b, c);
System.out.println(test1.test("cnsuning.com", 2, 8));
MyTest test2 = String::substring;
System.out.println(test2.test("cnsuning.com", 2, 8));
System.out.println("************************");
// 4.引用构造器
// 类名::new
// 函数式接口中被实现的方法的全部参数传给该构造器作为参数
MyTest2 test3 = username -> new Person(username);
System.out.println(test3.test("jack"));
MyTest2 test4 = Person::new;
System.out.println(test4.test("jack"));
}
}
@FunctionalInterface
interface Converter {
Integer valueOf(String value);
}
@FunctionalInterface
interface MyTest {
String test(String a, int b, int c);
}
class Person {
private String username;
public Person(String username) {
this.username = username;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("username='").append(username).append('\'');
sb.append('}');
return sb.toString();
}
}
@FunctionalInterface
interface MyTest2 {
Person test(String username);
}
3.Lambda表达式和匿名内部类的区别:
相同点:
- Lambda表达式和匿名内部类都可以访问局部变量以及外部类的成员变量
- Lambda表达式创建的对象和匿名内部类创建的对象都可以直接调用从接口中继承的默认方法
区别:
- 匿名内部类可以为任意接口、抽象类、普通类创建实例,不管接口包含多少个抽象方法;但Lambda只能为函数式接口创建实例
- 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法,但是Lambda不可以