Lambda表达式不是Java最早使用的,很多语言就支持Lambda表达式,例如:C++,C#,Python,Scala等。如果有Python或者Javascript的语言基础,对理解Lambda表达式有很大帮助,可以这么说lambda表达式其实就是实现SAM接口的语法糖,使得Java也算是支持函数式编程的语言。Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。
1、Lambda表达式引入
示例1:Runnable实现线程
public void test1(){
new Thread(new Runnable(){
public void run(){
System.out.println("do something..");
}
}).start();
}
public void test2(){
new Thread(() -> System.out.println("do something..")).start();
}
示例2:foreach遍历
public void test3(){
List<String> list = Arrays.asList("hello","java","bdit","lambda");
for (String string : list) {
System.out.println(string);
}
}
public void test4(){
List<String> list = Arrays.asList("hello","java","bdit","lambda");
list.forEach(System.out::println);
}
示例3:FileFilter文件过滤
public void test5(){
//1.文件目录
File fileDir=new File("D:/resource");
//2.创建筛选规则,帅选出所有.java文件
FileFilter filter=new FileFilter() {
@Override
public boolean accept(File file) {
if(!file.isDirectory()&&file.getName().endsWith(".java")){
return true;
}
return false;
}
};
//3.得到筛选文件
File[] files=fileDir.listFiles(filter);
for (File file : files) {
System.out.println(file);
}
}
public void test6(){
//1.文件目录
File fileDir=new File("D:/resource");
//2.得到筛选文件,帅选出所有.java文件
File[] files=fileDir.listFiles((file) -> !file.isDirectory() && file.getName().endsWith(".java"));
//3、遍历查看结果
Arrays.asList(files).forEach(System.out::println);
}
2、函数式接口概念
Lambda表达式是用来实现SAM接口的,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。
其实只要满足“SAM”特征的接口都可以称为函数式接口,但是如果要更明确一点,最好在声明接口时,加上@FunctionalInterface。
JDK1.8之前,核心类库中就已经存在很多SAM接口了,例如:
(1)java.lang.Runnable
(2)java.util.concurrent.Callable
(3)java.util.Comparator
(4)java.lang.Comparable
(5)java.lang.Iterable
(6)java.io.FileFilter
(7)java.lang.reflect.InvocationHandler
…等
但是在JDK1.8,只有(1)(2)(3)(6)加了@FunctionalInterface,那些没有加@FunctionalInterface的SAM接口,现在使用Lambda表达式实现,但是存在将来增加抽象方法变成非SAM接口的风险,因此建议只对加了@FunctionalInterface的接口使用Lambda表达式实现。
JDK1.8在java.util.function包增加了很多函数式接口,不过他们可以归纳为四类:消费型接口、供给型接口、功能型接口、判断型接口,一共43个,基本上可以满足开发中函数式接口的基本使用需求,如你在开发中需要设计函数式接口,请先从以下接口中选择是否有满足需求的,如果有几不需要重新设计了。
1、消费性接口
这类接口的抽象方法特点:有形参,但是返回值类型是void
接口名 | 抽象方法 | 描述 |
---|---|---|
Consumer< T> | void accept(T t) | 接收一个对象用于完成功能 |
BiConsumer<T,U> | void accept(T t, U u) | 接收一个对象用于完成功能 |
DoubleConsumer | void accept(double value) | 接收一个double值 |
IntConsumer | void accept(int value) | 接收一个int值 |
LongConsumer | void accept(long value) | 接收一个long值 |
ObjDoubleConsumer< T> | void accept(T t, double value) | 接收一个对象和一个double值 |
ObjIntConsumer< T> | void accept(T t, int value) | 接收一个对象和一个int值 |
ObjLongConsumer< T> | void accept(T t, long value) | 接收一个对象和一个long值 |
2、供给型接口
这类接口的抽象方法特点:无参,但是有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
Supplier< T> | T get() | 返回一个对象 |
BooleanSupplier | boolean getAsBoolean() | 返回一个boolean值 |
DoubleSupplier | double getAsDouble() | 返回一个double值 |
IntSupplier | int getAsInt() | 返回一个int值 |
LongSupplier | long getAsLong() | 返回一个long值 |
3、判断型接口
这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。
接口名 | 抽象方法 | 描述 |
---|---|---|
Predicate< T> | boolean test(T t) | 接收一个对象 |
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个对象 |
DoublePredicate | boolean test(double value) | 接收一个double值 |
IntPredicate | boolean test(int value) | 接收一个int值 |
LongPredicate | boolean test(long value) | 接收一个long值 |
4、功能型接口
这类接口的抽象方法特点:既有参数又有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
Function<T,R> | R apply(T t) | 接收一个T类型对象,返回一个R类型对象结果 |
UnaryOperator< T> | T apply(T t) | 接收一个T类型对象,返回一个T类型对象结果 |
DoubleFunction< R> | R apply(double value) | 接收一个double值,返回一个R类型对象 |
IntFunction< R> | R apply(int value) | 接收一个int值,返回一个R类型对象 |
LongFunction< R> | R apply(long value) | 接收一个long值,返回一个R类型对象 |
ToDoubleFunction< T> | double applyAsDouble(T value) | 接收一个T类型对象,返回一个double |
ToIntFunction< T> | int applyAsInt(T value) | 接收一个T类型对象,返回一个int |
ToLongFunction< T> | long applyAsLong(T value) | 接收一个T类型对象,返回一个long |
DoubleToIntFunction | int applyAsInt(double value) | 接收一个double值,返回一个int结果 |
DoubleToLongFunction | long applyAsLong(double value) | 接收一个double值,返回一个long结果 |
IntToDoubleFunction | double applyAsDouble(int value) | 接收一个int值,返回一个double结果 |
IntToLongFunction | long applyAsLong(int value) | 接收一个int值,返回一个long结果 |
LongToDoubleFunction | double applyAsDouble(long value) | 接收一个long值,返回一个double结果 |
LongToIntFunction | int applyAsInt(long value) | 接收一个long值,返回一个int结果 |
DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个double值,返回一个double |
IntUnaryOperator | int applyAsInt(int operand) | 接收一个int值,返回一个int结果 |
LongUnaryOperator | long applyAsLong(long operand) | 接收一个long值,返回一个long结果 |
BiFunction<T,U,R> | R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 |
BinaryOperator< T> | T apply(T t, T u) | 接收两个T类型对象,返回一个T类型对象结果 |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U u) | 接收一个T类型和一个U类型对象,返回一个double |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | 接收一个T类型和一个U类型对象,返回一个int |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | 接收一个T类型和一个U类型对象,返回一个long |
DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个double值,返回一个double结果 |
IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个int值,返回一个int结果 |
LongBinaryOperator | long applyAsLong(long left, long right) | 接收两个long值,返回一个long结果 |
3、Lambda表达式
Lambda表达式是用来实现SAM接口的,它相当于一个匿名函数,语法格式如下:
(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
简单的说就是:
(形参列表) -> {
Lambda体}
这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
(1)左侧:指定了 Lambda 表达式需要的参数列表,它其实就是函数式接口的抽象方法的形参列表
(2)右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能,它其实就是实现函数式接口的抽象方法的方法体。
例如:Lambda表达式只写了Runnable接口的抽象方法public void run()的()空参列表以及对run()方法的实现代码。
public void test1(){
new Thread(new Runnable(){
public void run(){
System.out.println("do something..");
}
}