拿之前排序的方法为例,首先我们用匿名类来实现:
Arrays.sort(numbers, new Comparator<Integer>() { @Override public int compare(Integer first, Integer second) { return first.compareTo(second); } });下一步,去掉实例和方法签名:
Arrays.sort(numbers, (Integer first, Integer second) { return first.compareTo(second); });引用 Lambda 表达式:
Arrays.sort(numbers, (Integer first, Integer second) -> { return first.compareTo(second); });完成!但有些地方可以进一步优化。你可以去掉参数的类型,编译器已经足够聪明知道参数的类型。
Arrays.sort(numbers, (first, second) -> { return first.compareTo(second); });如果是一个简单的表达式的话,例如只有一行代码,你可以去掉方法体的大括号,如果有返回值的话,return 关键字也可以去掉。
Arrays.sort(numbers, (first, second) -> first.compareTo(second));如果Lambda 只有一个参数的话,参数外面的小括号也可以去掉。
(x) -> x + 1去掉小括号后,
x -> x + 1下一步我们做下总结,
(int x, int y) -> { return x + y; } (x, y) -> { return x + y; } (x, y) -> x + y; x -> x * 2 () -> System.out.println("Hello"); System.out::println;第一个方式是完整的 Lambda 的声明和使用的方式,不过有些冗余,其实,参数的类型可以省略;
第二个方式是去掉参数类型的 Lambda 表达式;
第三个方式是,如果你的方法体只有一行语句,你可以直接省略掉大括号和 return 关键字;
第四个方式是没有参数的 Lambda 表达式;
第五个方式是Lambda 表达式的变种:是Lambda 表达式的一种简写,称为方法引用。例如:
实际上它是下面Lambda 表达式的一种简写:
(value -> System.out.prinltn(value) 深入 Lambda表达式 函数式接口Java 把 Lambda表达式当作是一个接口类型的实例。它把这种形式被称之为函数式接口。一个函数式接口就是一个只有单一方法的接口,Java把这种方法称之为“函数式方法”,但更常用的名字为单一抽象方法(single abstract method" 或 SAM)。例如JDK中存在的接口例如Runnable和Callable。
@FunctionalInterfaceOracle 引入了一个新的注解为@FunctionalInterface, 用来标识一个接口为函数式接口。它基本上是用来传达这一用途,除此而外,编辑器还会做一些额外的检查。
比如,下面的接口:
如果加上@FunctionalInterface注解,则会编译错误:
@FunctionalInterface // <- error here public interface FunctionalInterfaceExample { // doesn't compile }编译器就会报错,错误的详细信息为“Invalid '@FunctionalInterface' annotation; FunctionalInterfaceExample is not a functional interface”。意思是没有定义一个单一的抽象方法。
而如果我们定义了两个抽象方法会如何?
编译器再次报错,提示为"multiple, non-overriding abstract methods were found"。所以,一旦使用了此注解,则在接口里只能定义一个抽象方法。
而现在有这样一种情况,如歌一个接口继承了另一个接口,会怎么办?我们创建一个新的函数式接口为A,定义了另一个接口B,B继承A,则B仍然是一个函数式接口,它继承了A的apply方法。
@FunctionalInterface interface A { abstract void apply(); } interface B extends A {如果你想看起来更加清晰,可以复写父类的方法:
@FunctionalInterface interface A { abstract void apply(); } interface B extends A { @Override abstract void apply(); }我们可以用下面的代码来测试一下上面的两个接口是否为函数式接口:
@FunctionalInterface public interface A { void apply(); } public interface B extends A { @Override void apply(); } public static void main(String... args) { A a = () -> System.out.println("A"); B b = () -> System.out.println("B"); a.apply(); // 打印:A b.apply(); // 打印:B }如果B接口继承了A接口,那么在B接口中就不能定义新的方法了,否则编译器会报错。
除了这些,在Java 8 中接口有了一些新的改进:
方法引用简单来说,方法引用就是 Lambda 表达式的一种简写。当你创建一个 Lambda 表达式时,你创建了一个匿名方法并提供方法体,但你使用方法引用时,你只需要提供已经存在的方法的名字,它本身已经包含方法体。
它的基本语法如下;
或一个更加简洁明了的例子:
String::valueOf"::"符号前面表示的是目标引用,后面表示方法的名字。所以,在上面的例子,String 类作为目标类,用来寻找它的方法valueOf,我们指的就是 String 类上的静态方法。
public static String valueOf(Object obj) { ... }"::"称之为定界符,当我们使用它的时候,只是用来引用要使用的方法,而不是调用方法,所以不能在方法后面加()。
String::valueOf(); // error
你不能直接调用方法引用,只是用来替代 Lambda 表达式,所以,哪里使用 Lambda 表达式了,哪里就可以使用方法引用了。
所以,下面的代码并不能运行:
这是因为该方法引用不能转化为Lambda 表达式,因为编译器没有上下文来推断要创建哪种类型的Lambda。
我们知道这个引用其实是等同于下面的代码:
但编译器还不知道。虽然它可以知道一些事情。它知道,作为一个Lambda,返回值应该是字符串类型,因为valueOf方法的返回值为字符串类型。但它不知道作为论据需要提供什么信息。我们需要给它一点帮助,给它更多的上下文信息。
下面我们创建一个函数式接口Conversion,
接下来我们需要创建一个场景去使用这个接口作为一个 Lambda,我们定义了下面的方法:
public static String convert(Integer number, Conversion function) { return function.convert(number); }