lambda表达式
(一)为什么要引入lambda表达式?
lambda表达式是一个匿名函数,可以把lambda表达式理解成一段可以像数据一样传递的代码块。目的是更简洁,更灵活。
lambda表达式可以用更简洁的方式来创建只有一个抽象方法的接口(函数式接口)的实例。
我们可以先来看看下面的代码:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String s1,String s2) {
return Integer.compare(s1.length(), s2.length());
}
};
上面的代码中,就是一个匿名内部类的方式创建Comparator对象comp,执行的代码也仅仅是Integer.compare(s1.length(),s2.length());这一段,那么我们使用lambda表达式的形式来看:
Comparator<String> comp = (x,y)->Integer.compare(x.length(),y.length());
从上面可以看出,lambda表达式更简洁,更灵活。
(二)lambda表达式的语法
lambda表达式形式:参数,箭头(->),以及一个表达式或代码块
基本语法
(parameters) -> expression
或
(parameters) ->{ statements; }
格式一:无参数,无返回值
()->System.out.println("Hello,World");
格式二:有一个参数,无返回值
(x)->System.out.println(x);
格式三:若只有一个参数,小括号可以不写
x->System.out.println(x);
格式四:有两个以上参数,有返回值,并且lambda体中有多条语句
Comparator<Integer> comp = (x,y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
格式五:若lambda里只有一条语句,大括号和return都不需要写
Comparator<Integer> comp = (x,y)->Integer.compare(x,y);
格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
接下来看看之前写过的比较器,用lambda表达式来实现:
package lambda;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaTest {
public static void main(String[] args) {
String[] s = {"abcde","bcde","a","cde","ab"};
Comparator<String> comp = (x,y)->Integer.compare(x.length(), y.length());
Arrays.sort(s, comp);
for(String e : s) {
System.out.println(e);
}
}
}
(三)函数式接口
什么是函数式接口?
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口就称为函数式接口。
注意:在Java8中,默认方法和静态方法都不是抽象方法;并且接口继承Object,如果接口声明了Object类的方法,也不算是抽象方法。
函数式接口有一个注解@FunctionalInterface,这个注解只能标记在有且只有一个的接口上。当然并不是函数式接口就必须要用它标记,也可以不进行标记。如果你所写的不是函数式接口,用了这个标记,编译器就会报错。
package lambda;
@FunctionalInterface
public interface FunctionalInterfaceTest {
//抽象方法,有且只有一个
int sub();
//默认方法,可以有
default int multiply(int x,int y) {
return x*y;
}
//Object类的方法
public boolean equals(Object obj);
//Java8中可以声明静态方法
public static void add() {
System.out.println("Hello");
}
}
(四)方法引用
有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。这时可以使用方法引用。
方法的引用有三种形式:
①Class::staticMethod ;②object::instanceMethod;③Class::instanceMethod。
种类 | 示例 | 说明 | 对应的lambda表达式 |
引用类方法 | 类名::类方法 | 函数式接口中被实现方法的全部参数传给该类方法作为参数 | (a,b,...)->类名.类方法(a,b,...) |
引用特定对象的实例方法 | 特定对象::实例方法 | 函数式接口中被实现方法的全部参数传给该方法作为参数 | (a,b,...)->特定对象.实例方法(a,b,...) |
引用某类对象的实例方法 | 类名::实例方法 | 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 | (a,b,...)->a.实例方法(b,...) |
引用构造器 | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | (a,b,...)->new 类名(a,b,...) |
1.引用类方法 例如:Math::pow等价于(x,y)->Math.pow(x,y);
2.引用特定对象的实例方法 例如: System.out::println等价于(x)->System.out.println(x);
3.引用某类对象的实例方法 例如: String::compareToIgnoreCase等价于(x,y)->x.compareToIgnoreCase(y);
同样的,this和super也可以使用方法。例如:this::equals等等。
(五)构造器引用
构造器引用和方法引用类似,方法名变成了new关键字。我们看看代码:
package lambda;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConstructorTest {
public static void main(String[] args) {
//下面是传统的lambda表达式和构造器引用的比较
//传统lambda表达式
Supplier<Employee> sup = ()->new Employee();
//①无参构造器
Supplier<Employee> sup2 = Employee::new;
//②一个参数的构造器
Function<String,Employee> fun = Employee::new;
System.out.println(fun.apply("a"));
//③两个参数的构造器
BiFunction<String,Integer,Employee> bi = Employee::new;
System.out.println(bi.apply("b", 20));
}
}
class Employee{
private String name;
private int id;
private int age;
private double salary;
public Employee() {
}
public Employee(String name) {
this.name=name;
}
public Employee(String name,int age) {
this.name=name;
this.age=age;
}
public Employee(String name,int age,int id,double salary) {
this.name=name;
this.age=age;
this.id=id;
this.salary=salary;
}
public String toString() {
return getClass().getName()+"[name="+name+",age="+age+
",id="+id+",salary="+salary+"]";
}
}
lambda表达式还有很多类型,接下来在熟悉内部类,集合批处理等操作后,再来看看lambda表达式还有哪些用法。