JDK1.8新特性之Lambda表达式
1 Lambda表达式
1.1 Lambda表达式初认识
lambda表达式一般用于匿名实现某种接口的方法。
比方说使用Runnable这个接口,当我们采用匿名类创建这个对象时通常是以下这种写法
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
但当我们使用lambda表达式来写的话就可以简化成一行代码。如下:
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
1.2 Lambda表达式的使用格式
格式: (参数) -> 表达式
1.3 Lambda表达式的基础语法
Java8中引入了一个新的操作符"->" 该操作符称为箭头操作符或 Lambda操作符,箭头操作符将Lambda表达式拆分成两个部分:左侧和右侧
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需执行的功能,Lambda体(其实就是方法体)
1.3.1 语法格式一:实现无参数,无返回值的接口函数
不使用Lambda表达式之前:
public void test1(){
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
r.run();
}
使用Lambda表达式:
public void test1(){
Runnable r = () -> System.out.println("Hello Lambda");
r.run();
}
1.3.2 语法格式二:实现有参数,无返回值的接口函数(若只有一个参数,左侧小括号可以省略不写)
public static void main(String[] args) {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("加油每一天");
Consumer<String> con2 = x -> System.out.println(x);
con2.accept("加油每一周");
}
1.3.3 语法格式三:有两个以上的参数,有返回值,并且Lambda体中有多行语句。
public static void main(String[] args) {
Comparator<Integer> com = (x,y) -> { //函数体内有多行语句时必须要用花括号
System.out.println("函数式接口");
return Integer.compare(x,y);
};
}
1.3.4 语法格式四:有两个以上的参数,有返回值,而Lambda体中只有一条语句,return和大括号都可以省略不写。
public static void main(String[] args) {
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
}
1.3.5 语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”
举个例子:
Consumer<String> con = (x) -> System.out.println(x);
这句语句中括号中的x是不是没有写类型,但其实在前面的接收变量con以及声明好了String类型,也就相当于声明了在使用accept函数时所输入的参数类型是String。
1.4 Lambda表达式使用场景说明
首先说明Lambda表达式是用于简化实现接口而使用的。当一个接口中有且只有一个(包括父接口有抽象方法也不行)抽象方法时(除了存在于Object类中的方法和default方法和静态方法),可以使用Lambda表达式来简化匿名实现接口中的抽象方法。
1.5 @FunctionalInterface究竟表示了什么?
其实一个接口被注解上@FunctionalInterface就意味着该接口符合使用Lambda表达式的条件,就把该接口称为函数式接口。
1.6 Java8中内置的四大核心函数式接口
- Consumer<T> : 消费型接口,当中提供的方法是void accept(T t),有参数无返回值。
- Supplier<T> : 供给型接口,当中提供的方法是T get(),无参数有返回值
- Function<T, R> : 函数型接口,当中提供的方法是R apply(T t),有参数有返回值
- Predicate<T> : 断言型接口,当中提供的方法是boolean test(T t),用于做一些判断
1.6.1 其他一些内置函数式接口
1.7 Lambda表达式的方法引用
1.7.1 如何理解方法引用
方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用是Lambda表达式的另外一种表现形式)
1.7.2 方法引用的三种语法格式
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
1.7.3 详细讲解方法引用如何使用
1.7.3.1 对象::实例方法名
现在的需求是:我需要在Lambda体中调用System.out.Println()
我们在学了Lambda表达式后可以像下面这么写:
public static void main(String[] args) {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("Hello");
}
但我们在上面讲到方法引用的使用场景是Lambda体中的功能有方法已经实现,就可以用方法引用,这里我们代码中的Lambda体内容是System.out.println(),我们知道System.out.println();本身的System.out就会产生一个PrintStream类型对象并调用当中的println()方法,因此上面的代码可以使用方法应用来简写成以下形式:
public static void main(String[] args) {
Consumer<String> con = System.out::println;
con.accept("Hello");
}
使用方法引用的前提:
以上面代码举例,当你要使用Consumer当中accept这个方法时,这个函数式接口方法的参数和返回值必须和你采用方法引用所引用的方法的参数和返回值一致,以上例子来说也就是Consumer中的accept的参数是String,返回值是void,那么方法引用println的参数也是String,返回是void,那么满足条件即可使用方法引用
1.7.3.2 类::静态方法名
需求:我们需要使用Comparator这个抽象类方法:int compare(T o1, T o2);实现比对两个int类型的大小,下面是未使用方法引用的写法:
public static void main(String[] args) {
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
}
我们可以发现在Lambda体中调用Integer.compare是静态方法调用,并且compare的参数和返回值和Comparator中的int compare(T o1, T o2);保持一致,可以使用方法引用写:
public static void main(String[] args) {
Comparator<Integer> com = Integer::compare;
}
1.7.3.3 类::实例方法名
需求:使用函数式接口的方法比较两个字符串是否一样。我们可以使用BiPredicate这个内置函数式接口中的方法。未使用方法引用写法如下:
public static void main(String[] args) {
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
}
使用方法引用可以这样写:
public static void main(String[] args) {
BiPredicate<String,String> bp = String::equals;
}
可能会有人问equals并不是静态方法为什么也能直接使用类进行调用呢?这是有条件的,如果lambda表达式的第一个参数x是这个equals实例方法的调用者,而第二个参数y是这个实例方法的参数时,我们就可以使用类名::实例方法名。
1.8 Lambda表达式的构造器引用
格式:ClassName::new
需求:现在我们要使用Supplier供给型接口提供相应对象,未使用构造器引用是像以下写法:
public static void main(String[] args) {
Supplier<Apple> apple = () -> new Apple();
}
我们还可以使用构造器引用方式
public static void main(String[] args) {
//构造器引用方式
Supplier<Apple> apple = Apple::new;
}
这里Apple::new是调用Apple类的无参构造器进行创建的,因为是取决于函数式接口中的方法,这里以Supplier的get()方法为准,get是无参,所以对应的Apple::new也是调用无参构造器创建。
1.9 Lambda表达式的数组引用
格式:Type[]::new
public static void main(String[] args) {
Function<Integer,String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
}
下面是使用数组引用后,
public static void main(String[] args) {
Function<Integer,String[]> fun = String[]::new;
String[] strs = fun.apply(10);
System.out.println(strs.length);
}