在学习Lambda表达式之前,先了解什么是函数式接口
函数式接口:对于只有一个抽象方法的接口就叫函数式接口。
Java JDK1.8时引入了java.util.function包,其中有许多通用的函数式接口。
下面将简单介绍常用的函数式接口
函数式接口 | 参数类型 | 返回类型 | 抽象方法名 | 描述 |
---|---|---|---|---|
Supplier |
无 | T | get | “提供”一个T类型的值 |
Consumer |
T | void | accept | “消费”(处理)一个T类型的值 |
Function |
T | R | apply | 对T类型参数操作,返回一个R类型参数 |
Predicate |
T | boolean | test | 对类型T进行筛选,返回一个布尔值 |
在使用Lambda表达式时,最好把它看作是一个函数而不是一个对象
实际上,在Java中,对Lambda表达式所能做的也只是能转换为函数式接口
这是一个测试用的JavaBean
class Person
{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class TestLambda {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
//往集合添加元素
list.add(new Person("小华",16));
list.add(new Person("小明",17));
list.add(new Person("小王",18));
//在不使用Lambda表示式时,需要的参数是一个接口实现类或对象时我们通常的方法时使用匿名内部类
//此处是筛选出年龄大于等于18的人
TestLambda.filter(list, new Predicate<Person>() {
public boolean test(Person person) {
if(person.getAge()>=18)
return true;
return false;
}
});
//使用Lambda表达式
TestLambda.filter(list, p->p.getAge()>=18);
}
//筛选出符合条件的值,并打印该值
public static <T> void filter(List<T> list,Predicate<T> pre) {
for(T t:list) {
if(pre.test(t))
System.out.println(t);
}
}
两次打印的结果是一样的
可以看到使用了Lambda表达式后代码简洁了许多,提高了程序的可读性
静态方法filter需要两个参数,一个是List集合和实现Predicate接口的对象,而它们都指定了泛型
那编译器是怎么确定 p->p.getAge()>=18 这行代码的p是什么类型的呢
在这里编译器可以推到出p必然是Person类型,因为list是一个指定了类型参数为Person的集合
所以在这里就不用写出(Person p),可以省略掉参数类型,又因为test方法只有一个参数,可以把括号省略,并且方法体只有一行语句,{}省略掉,return关键字也可以省略
public class TestLambda {
public static void main(String[] args) {
List<Person> list = null;
//使用Lambda表达式
list = getObject(()->new ArrayList<>()); //返回一个ArrayList对象
list.add(new Person("小华",16));
System.out.println(list);
//使用匿名内部类
list = getObject(new Supplier<List>() {
public List get() {
return new ArrayList<Person>(); //同样返回一个ArrayList对象
}
});
list.add(new Person("小明",17));
System.out.println(list);
//使用Lambda表达式返回不一样的结果
Person p = getObject(()->new Person("小张",15));
System.out.println(p);
}
//一个参数为实现了Supplier接口的对象
public static <T> T getObject(Supplier<T> sup) {
return sup.get(); //调用get抽象方法,不同的实现会返回不同的结果
}
}
Supplier接口是供给型接口,无参数,返回类型为T
但仍要提供空括号,就像无参数方法一样
由于方法体只有一条语句,同样可以省略{}和return
->右边就是要”供给“的物资,上面的例子中分别”供给“了两个ArrayList集合和一个Person
public class TestLambda {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("小王",16));
list.add(new Person("小张",12));
list.add(new Person("小唐",18));
//使用Lambda表达式
printList(list,p->System.out.println(p));
//使用匿名内部类
printList(list,new Consumer<Person>() {
public void accept(Person p) {
System.out.println(p);
}
});
}
//打印list中的每一个元素
public static <T> void printList(List<T> list,Consumer<T> con) {
for(T t:list) {
con.accept(t);
}
}
}
Consumer接口是消费型接口,需要传入一个参数,无返回值
这里Lambda表达式的格式和上一个没有什么不同,这里不再赘述
System.out.println( p )这一步就是accept的方法体,编译器怎么知道p是Person类型的呢?
由于list已经指定泛型为Person,并且printList方法list的元素与accept是关联的,
所以accept接收的参数p推断出也是Person类型
省去了像匿名内部类一样需要显示的声明Consumer的T为Person
public class TestLambda {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("小王",16));
list.add(new Person("小张",12));
list.add(new Person("小唐",18));
//使用匿名内部类,完成排序
Collections.sort(list, new Comparator<Person>() {
public int compare(Person p1, Person p2) {
if(p1.getAge()==p2.getAge())
return p1.getName().compareTo(p2.getName());
return Integer.compare(p1.getAge(), p2.getAge());
}
});
System.out.println(list);
//使用Lambda表达式,完成排序
Collections.sort(list, (p1,p2)->{
if(p1.getAge()==p2.getAge())
return p1.getName().compareTo(p2.getName());
return Integer.compare(p1.getAge(), p2.getAge());
});
System.out.println(list);
}
}
Comparetor比较接口,它本身支持泛型,有一个抽象方法int compare(T o1,T o2)
compare方法不是立即调用,实际上在完成排序之前,sort方法会一直调用compare方法,只要元素的顺序不正确就会重新排列元素。
先分析Lambda表达式,由于需要两个参数,所以要用括号括起来并用逗号分隔开(p1,p2),
不用写参数类型的原因是,编译器可以自行推断出来,因为list的类型参数是Person,
所以Comparator的参数类型也必然是Person,很多时候编译器都都能通过上下文推断类型参数。
这里表达式特殊之处是compare的方法体不止一条语句,所以要加{},并且return不可以省略,还要注意分号