Java8 引入了 Lambda 表达式,使用 Lambda 表达式可以让代码更加简洁。Lambda 表达式其实也就是一个匿名函数,我们可以用它去代替匿名函数,我们先来看一个例子
1、Lambda 表达式语法
我们用接口 Runnable 举个例子
public static void main(String[] args) {
// 匿名函数
new Thread(new Runnable() {
public void run() {
System.out.println("anonymous function");
}
}).start();
// lambda 表达式
new Thread(() -> System.out.println("lambda")).start();
}
从上的例子可以看出我们使用 () -> {} 的代码块代替了整个匿名函数
Lambda 表达式的语法格式如下
( parameters ) -> expression
( parameters ) -> {statements;}
- 可以不需要声明参数类型,编译器可以统一识别参数值。
- 一个参数时可以不定义圆括号,但多个参数需要定义圆括号。
- 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值(如果接口方法返回类型为 void 则不用)。
Lambda 表达式的简单例子
// 无参数,返回值为 10
() -> 10
// 单参数(数字类型),返回其2倍的值
x -> 2 * x
// 多个参数(数字),并返回他们的差值
(x, y) -> x – y
// 声明参数类型,返回他们的和
(int x, int y) -> return x + y
// 声明参数类型,并在控制台打印,不返回任何值(接口方法返回类型为 void)
(String s) -> System.out.print(s)
2、Lambda 行为参数化和变量作用域
我们可以将lambda表达式作为参数传递给方法,也就是说允许把函数作为一个方法的参数。
创建一个 Calculator 的函数接口。 在 Calculator 中有一个称为 calculate 的方法,它接受两个 int 参数并返回一个 int 值。 在 engine 方法中,它接受函数接口 Calculator 作为参数。在主方法中,用不同的lambda表达式调用 engine 方法四次
public class LambdaTest {
public static void main(String[] argv) {
engine((x, y) -> x + y);
engine((x, y) -> x * y);
engine((x, y) -> x / y);
engine((x, y) -> x % y);
}
private static void engine(Calculator calculator) {
int x = 2, y = 4;
int result = calculator.calculate(x, y);
System.out.println(result);
}
}
@FunctionalInterface
interface Calculator {
int calculate(int x, int y);
}
lambda 表达式不定义自己的范围。 如果我们在 lambda 中使用关键字
this
和super
,this
代表着 lambda 表达式所被包含的类
变量作用域
lambda 表达式与其外部方法具有相同的范围。 lambda表达式不会创建自己的作用域。
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
public static void main(String args[]) {
final int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // 输出结果为 3
}
public interface Converter<T1, T2> {
void convert(int i);
}
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
// Local variable num defined in an enclosing scope must be final or effectively final
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
3、函数式接口
函数式接口只是具有一个方法的接口,用作 lambda 表达式的类型。
通常会使用 @FunctionalInterface 注解来标注了函数式接口
在 java8 中为我们定义了很多常用的函数式接口,它们都放在java.util.function包下面,一般有以下常用的四大核心接口
函数式接口 | 参数类型 | 返回类型 | 描述 |
---|---|---|---|
Consumer<T>(消费型接口) | T | void | 对类型为T的对象应用操作。void accept(T t) |
Supplier<T>(供给型接口) | 无 | T | 返回类型为T的对象。 T get(); |
Function<T, R>(函数型接口) | T | R | 对类型为T的对象应用操作并返回R类型的对象。R apply(T t); |
Predicate<T>(断言型接口) | T | boolean | 确定类型为T的对象是否满足约束。boolean test(T t); |
4、方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 ::
1)构造函数引用
使用构造函数引用的语法是 ClassName::new
Function<String,String> func1 = str -> new String(str);
Function<String,String> func2 = String::new;
2) 静态方法引用
方法引用的一般语法是 Qualifier::MethodName
Function<Integer, String> func1 = x -> Integer.toBinaryString(x);
Function<Integer, String> func2 = Integer::toBinaryString;
3) 实例方法引用
Supplier<Integer> supplier = () -> "www.w3cschool.cn".length();
Supplier<Integer> supplier1 = "www.w3cschool.cn"::length;
4) 通用方法引用
Function<String[],List<String>> asList = Arrays::<String>asList;
System.out.println(asList.apply(new String[]{"a","b","c"}));