Java lambda表达式
1. Java函数式接口
Java实现函数式编程的方式是函数式接口(functional interface),函数式接口简单来说,是只包含一个抽象方法的接口。比如Java标准库中的java.lang.Runnable和 java.util.Comparator都是典型的函数式接口。
java 8提供 @FunctionalInterface作为函数式接口的注解,该注解非强制使用,主要防止团队的其他人员错误地往接口中添加新的方法。
2. lambda表达式
lambda表达式是匿名方法,是函数式接口的匿名实现。
使用lambda表达式实现Runnable例子:
// 使用匿名内部类
new Thread(new Runnable() {
public void run() {
System.out.println("Hello world !");
}
}).start();
// 使用 lambda 表达式
new Thread(() -> System.out.println("Hello world !")).start();
3. lambda语法
(parameters) -> expression
或者
(parameters) -> { statements; }
JButton jb=new JButton();
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("press button");
}
});
jb.addActionListener((ActionEvent e)->{System.out.println("press button");});
jb.addActionListener((e)->{System.out.println("press button");});
jb.addActionListener((x)->System.out.println("press button"));
jb.addActionListener(x->System.out.println("press button"));
为了更好的支持lambda,Java8引入了一个新包java.util.function,它里面包含了常用的函数式接口,例如:
Predicate<T>——接收T对象并返回boolean
Consumer<T>——接收T对象,不返回值
Function<T, R>——接收T对象,返回R对象
Supplier<T>——提供T对象(例如工厂),不接收值
UnaryOperator<T>——接收T对象,返回T对象
BinaryOperator<T>——接收两个T对象,返回T对象
在参数为这些接口的地方,我们就可以直接使用lambda表达式了!
4. 方法引用(Method references)
lambda表达式允许我们定义一个匿名方法,并允许我们以函数式接口的方式使用它。我们也希望能够在已有的方法上实现同样的特性。
方法引用和lambda表达式拥有相同的特性(例如,它们都需要一个目标类型,并需要被转化为函数式接口的实例),不过我们并不需要为方法引用提供方法体,我们可以直接通过方法名称引用已有方法。
以下面的代码为例,假设我们要按照name为Person数组进行排序:
class Person {
private final String name;
private final int age;
public int getAge() { return age; }
public String getName() {return name; }
...
}
Person[] people = ...
Comparator<Person> byName = Comparator.comparing(p -> p.getName());
Arrays.sort(people, byName);
在这里我们可以用方法引用代替lambda表达式:
Comparator<Person> byName = Comparator.comparing(Person::getName);
方法引用的形式
方法引用的标准形式是:类名::方法名。(注意:只需要写方法名,不需要写括号)
有以下四种形式的方法引用:
引用静态方法
引用某个对象的实例方法
引用某个类型的任意对象的实例方法
引用构造方法
-
引用静态方法
ContainingClass::staticMethodName
例子: String::valueOf,对应的Lambda:(s) -> String.valueOf(s)
比较容易理解,和静态方法调用相比,只是把.换为:: -
引用特定对象的实例方法
containingObject::instanceMethodName
例子: x::toString,对应的Lambda:() -> this.toString()
与引用静态方法相比,都换为实例的而已 -
引用特定类型的任意对象的实例方法
ContainingType::methodName
例子: String::toString,对应的Lambda:(s) -> s.toString()
实例方法要通过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。 -
引用构造函数
ClassName::new
例子: String::new,对应的Lambda:() -> new String()
构造函数本质上是静态方法,只是方法名字比较特殊。
写个例子帮本节内容串一下帮助理解:
class Person {
Integer age;
public static int compareByAge(Person a,Person b){return a.age.compareTo(b.age);}
public int compareByAge2(Person b){return age.compareTo(b.age);}
public static int compareBy18(Person p){return p.age.compareTo(18);}
public int compareBy14(){return age.compareTo(14);}
}
public void sort(){
Person [] persons=new Person[10];
//使用匿名类
Arrays.sort(persons, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
return o1.age.compareTo(o2.age);
}
});
//使用lambda表达式
Arrays.sort(persons, (o1, o2) -> o1.age.compareTo(o2.age));
//使用lambda表达式和类的静态方法
Arrays.sort(persons, (o1, o2) -> Person.compareByAge(o1,o2));
//使用lambda表达式和实例方法
Arrays.sort(persons, (o1, o2) -> o1.compareByAge2(o2));
//使用方法引用
//引用的是类的静态方法
Arrays.sort(persons, Person::compareByAge);
//引用特定类型的任意对象的实例方法
Arrays.sort(persons, Person::compareByAge2);
//引用的是类的静态方法
Arrays.sort(persons,Comparator.comparing(Person::compareBy18));
//引用特定类型的任意对象的实例方法
Arrays.sort(persons,Comparator.comparing(Person::compareBy14));
}
`