Java8 Lambda表达式
Lambda简介
从Java8开始,Java引入了另外一个语法糖-Lambda表达式,Lambda表达式可以由编译器推断并转换包装为常规的代码,因此我们可以使用更少的代码来实现同样的功能。
Lambda的基本语法表示如下:
基本语法:
(parameters) -> expression
或
(parameters) ->{ statements; }
Lambda表达式是Java SE8的一个重要新特性,它允许我们通过表达式来代替功能接口。Lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体。
虽然Lambda表达式可以对某些接口进行简单实现,但是并不是所有的接口都可以使用Lambda表达式,Lambda 表达式规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
Java8中有另一个新特性:接口中可以提供方法的默认实现,使用default修饰的方法就是方法的默认实现,所以在外部看来就不是必须被实现的方法了,因此也不影响Lambda表达式的使用。
一般将可以使用Lambda表达式的接口称为函数式接口,函数式接口可以使用FunctionalInterface注解标记,FunctionalInterface注解具有一下特点。
- 该注解只能标记在"有且仅有一个抽象方法"的接口上。
- JDK8接口中的静态方法和默认方法,都不算是抽象方法。
- 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
- 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
如下两个接口都可以归类到函数式接口,因此都可以使用Lambda表达式表示。
@FunctionalInterface public interface MyFunctionalInterface { void method(); // Object中的方法 int hashCode(); } @FunctionalInterface public interface MyFunctionalInterface { void method(); default void method01(){ // ... } default void method02(){ // ... } }
Lambda示例
这里给出6个接口,后面的示例基于这几个接口演示。
// 无参无返回值 @FunctionalInterface public interface NoReturnNoParam { void method(); } // 一个参数无返回 @FunctionalInterface public interface NoReturnOneParam { void method(int a); } // 多参数无返回 @FunctionalInterface public interface NoReturnMultiParam { void method(int a, int b); } // 无参有返回 @FunctionalInterface public interface ReturnNoParam { int method(); } // 一个参数有返回值 @FunctionalInterface public interface ReturnOneParam { int method(int a); } // 多个参数有返回值 @FunctionalInterface public interface ReturnMultiParam { int method(int a, int b); } public class MainTest { public static void main(String[] args) { //无参无返回 NoReturnNoParam noReturnNoParam = () -> { System.out.println("NoReturnNoParam"); }; noReturnNoParam.method(); //一个参数无返回 NoReturnOneParam noReturnOneParam = (int a) -> { System.out.println("NoReturnOneParam param:" + a); }; noReturnOneParam.method(6); //多个参数无返回 NoReturnMultiParam noReturnMultiParam = (int a, int b) -> { System.out.println("NoReturnMultiParam param:" + "{" + a + "," + +b + "}"); }; noReturnMultiParam.method(6, 8); // 无参有返回值 ReturnNoParam returnNoParam = () -> { System.out.print("ReturnNoParam"); return 1; }; int res = returnNoParam.method(); System.out.println("return:" + res); // 一个参数有返回值 ReturnOneParam returnOneParam = (int a) -> { System.out.println("ReturnOneParam param:" + a); return 1; }; int res2 = returnOneParam.method(6); System.out.println("return:" + res2); // 多个参数有返回值 ReturnMultiParam returnMultiParam = (int a, int b) -> { System.out.println("ReturnMultiParam param:" + "{" + a + "," + b + "}"); return 1; }; int res3 = returnMultiParam.method(6, 8); System.out.println("return:" + res3); } }
Lambda语法简化。
public class MainTest { public static void main(String[] args) { //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写 NoReturnMultiParam lambda01 = (a, b) -> { System.out.println("简化参数类型"); }; lambda01.method(1, 2); //2.简化参数小括号,如果只有一个参数则可以省略参数小括号 NoReturnOneParam lambda02 = a -> { System.out.println("简化参数小括号"); }; lambda02.method(1); //3.简化方法体大括号,如果方法条只有一条语句,则可以省略方法体大括号 NoReturnNoParam lambda03 = () -> System.out.println("简化方法体大括号"); lambda03.method(); //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号 ReturnOneParam lambda04 = a -> a + 3; System.out.println(lambda04.method(5)); ReturnMultiParam lambda05 = (a, b) -> a + b; System.out.println(lambda05.method(1, 1)); } }
方法引用
Lambda表达式的使用不仅引入了运算符->
,而且还引入了另外一个新的运算符::
。双冒号::
运算符在Java8中被称为方法引用(method reference),方法引用是与Lambda表达式相关的一个重要特性。
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。
方法引用一般分为静态方法引用、实例方法引用、构造器方法引用。
静态方法引用和对象方法引用
public class MainTest { public static void main(String[] args) { // 静态方法引用 ReturnOneParam lambda01 = MainTest::addByTwo; System.out.println(lambda01.method(5)); // 对象方法引用 MainTest mainTest= new MainTest(); ReturnMultiParam lambda02 = mainTest::addMethod; System.out.println(lambda02.method(1, 1)); } private static int addByTwo(int x) { return x + 2; } private int addMethod(int x, int y) { return x + y; } }
但是在某个场景下,实例方法引用与静态方法引用使用方式却是一样的。若Lambda参数列表中的第一个参数是方法的参数调用者,而第二个参数是实例方法的参数时,可以使用实例方法引用。
BiPredicate<String, String> bp = (x, y) -> x.equals(y); BiPredicate<String, String> bp01 = String::equals; boolean test = bp01.test("xy", "xx"); System.out.println(test);
构造器方法引用
public class User { private String username; public User(String username) { this.username = username; } } @FunctionalInterface public interface BeanFactory { User create(String name); } // 构造器方法引用 BeanFactory beanFactory = User::new; User user = beanFactory.create("hello world");
其它方法引用示例
String[] array = {"aaa", "bbb", "ccc"}; List<String> list = Arrays.asList(array); list.forEach(System.out::println); Function<Integer, User[]> function = User[]::new; User[] array = function.apply(4);//这里的4是数组的大小 for(User e:array){ System.out.println(e);//如果输出的话,会输出4个空对象(null) } Supplier<List<User>> userSupplier = ArrayList::new; List<User> user = userSupplier.get();