1. 简单入门
1.1 概述
函数式接口:接口中仅且只有一个抽象方法,可用@FunctionalInterface注解标注接口,当多个抽象方法时会报错
Lambda:用于简化函数式接口抽象方法的实现,可替换匿名内部类的作用
1.2 入门例子
- 定义函数式接口
@FunctionalInterface // 注解非必须,只要保证接口只有一个抽象方法即可
interface Calculator{
int cal(int a,int b);
}
- 调用接口的抽象方法
public void invokeCal(int a,int b,Calculator calculator){
int result= calculator.cal(a,b);
System.out.println(result);
}
- 使用Lambda表达式简化抽象方法的实现
@Test
public void test(){
invokeCal(1,2,(int a,int b)->{return a+b;});
invokeCal(1,2,(int a,int b)->{return a-b;});
invokeCal(1,2,(int a,int b)->{return a*b;});
invokeCal(1,2,(int a,int b)->{return a/b;});
}
2. Lambda语法
2.1 语法说明
(形参列表) -> {Lambda体}
(int a)->{return a*10;}
- (形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄
- {Lambda体}就是实现这个抽象方法的方法体
- ->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)
2.2 语法优化
(int a)->return a*10
当{Lambda体}中只有一句语句时,可以省略{}和;(int a)->a*10
当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么return也可以省略,但是如果{;}没有省略的话,return是不能省略的(a)->a*10
(形参列表)的类型可以省略a->a*10
当(形参列表)的形参个数只有一个,那么可以把数据类型和()一起省略,但是形参名不能省略- 当(形参列表)是空参时,()不能省略
3. 四大内置函数式接口
3.1 消费型接口Consumer
Consumer:有参数,无返回值
Consumer接口源码
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
集合迭代接口Iterable应用了Consumer
public interface Iterable<T> {
default void forEach(Consumer<? super T> action)
}
测试
List<String> list= Arrays.asList("a","b","c");
// 参数l表示集合中每个元素
list.forEach(l-> System.out.println(l));
3.2 供给型接口Supplier
Supplier:无参,有返回值
Supplier接口源码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
数据流Stream类应用了Supplier
// 返回一个新的无限顺序无序的流
public static Stream<T> generate(Supplier<T> s)
测试
// Supplier
Stream<Double> stream = Stream.generate(() -> Math.random());
// Consumer
stream.forEach(s-> System.out.println(s));
3.3 功能型接口Function
Function:有参数,有返回值
Function接口源码
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Map集合应用了Function
// 如果Map中的key对应的value值为null,则调用Function
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
测试
Map<String,List> map=new HashMap<>();
// Function
// 如果Map中key为hobby的value为null,则把value=new ArrayList()
List<String> list=map.computeIfAbsent("hobby",key->{return new ArrayList(); });
list.add("篮球");
// BiConsumer<? super K, ? super V>
map.forEach((k,v)->System.out.println(k+":"+v));
3.4 判断型接口Predicate
Predicate:有参数,返回值为布尔类型
Predicate接口源码
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Collection集合应用了Predicate
// 满足条件则删除
public boolean removeIf(Predicate<? super E> filter) {
测试
List<Integer> list=new ArrayList<>(); // 不能用Arrays.asList
list.add(1);
list.add(2);
list.add(3);
// Predicate
list.removeIf(l->l<3);
list.forEach(l-> System.out.println(l));
4.Lambda引用简化
4.1 方法引用简化
前提
- Lambda体只有一句语句,并且该语句是通过调用外部现有的类或对象的方法来完成的
- 形参列表跟被调用类或对象的方法的形参列表一致;或者形参列表第一个形参是被调用对象的方法的调用者,形参列表后面的形参仍需要跟被调用对象的方法的形参列表一致;
简化
- 实际简化的是去掉lambda形参列表和调用方法的形参列表
语法
- 实例对象名::实例方法
- 类名::静态方法
- 类名::实例方法
测试
形参列表一致
List<Integer> list = Arrays.asList(1,2,3);
// lambda参数列表t与System.out的方法println()参数列表一致,可简化
list.forEach(t -> System.out.println(t));
// 对象方法引用简化,去掉了lambda参数列表t和println的参数列表
list.forEach(System.out::println);
形参列表一个参数是方法调用者
// lambda形参第一个参数x是equals方法的调用者,lambda形参其他参数y跟equals方法参数一致
BiPredicate<String,String> bi=(x, y)->x.equals(y);
// 简化,这里要使用lambda形参第一个参数的类String
BiPredicate<String,String> bi2=String::equals;
排序
List<Integer> list= Arrays.asList(5,6,2,8,1,4);
// 方式1:手写比较规则
list.sort((a,b)->a.compareTo(b));
// 方式2:方法引用简写
list.sort(Integer::compareTo);
// 方式3:使用Comparator接口
list.sort(Comparator.comparingInt(a->a));
list.forEach(System.out::println);
4.2 构造器简化
跟方法引用简化基本一样
类构造器简化
lambda形参列表与类构造方法参数列表一致
// lambda形参列表与File构造方法参数列表一致
Function<String,File> f1=s->new File(s);
// 简化
Function<String,File> f2=File::new;
File file = f2.apply("F:\\a.txt");
System.out.println(file.getName());
数组构造器简化
lambda参数作为数组创建时的长度参数
// lambda参数作为数组创建时的长度参数
Function<Integer,int[]> f1=l->new int[l];
// 简化
Function<Integer,int[]> f2=int[]::new;
int[] arr = f2.apply(5);
System.out.println(arr.length);