有且只有一个抽象方法的接口被称为函数式接口,该接口中可以包含其他的方法(默认,静态,私有)。函数式接口用@FunctionalInterface注解。
引入函数式编程后Java接口的定义发生了一些变化:
函数式接口:函数式接口中只能有一个抽象方法,这也是为了函数调用时避免带来二义性。使用@FunctionInterface标注接口为函数式接口(@FunctionInterface并不是一定要标注但若是标注可以在编译时就给你提示错误)
支持静态方法:支持静态方法目的完全出于编写类库,对某些行为进行抽象,但是接口中的静态方法不能被继承。
支持默认实现:这是不得已而为之,因为Java8引入了函数式接口,许多像Collection这样的基础接口中增加了方法,如果还是一个传统的抽象方法的话,那么可能很多第三方类库就会变得完全无法使用。新增一个方法所有实现类都要实现一次。被default修饰的方法–默认实现。其主要思想就是如果子类中没有实现,那么采用父类提供的默认实现。
定义了这种类型的接口,使得以其为参数的方法,可以在调用时,使用一个lambda表达式作为参数。从另一个方面说,一旦我们调用某方法,可以传入lambda表达式作为参数,则这个方法的参数类型,必定是一个函数式的接口。
关于函数式接口只有一个抽象方法
关于函数式接口只有一个抽象方法的理解:
一个函数式接口有且只有一个抽象方法。
默认方法不是抽象方法,因为它们已经实现了。
重写了超类Object类中任意一个public方法的方法并不算接口中的抽象方法。
如果一个接口中声明的抽象方法是重写了超类Object类中任意一个public方法,那么这些抽象方法并不会算入接口的抽象方法数量中。因为任何接口的实现都会从其父类Object或其它地方获得这些方法的实现。
所以比如Comparator接口,有两个抽象方法compare和equals,但equals并不算入接口中的抽象方法,所以Comparator接口还是满足函数式接口的要求,Comparator接口是一个函数式接口。
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
在Lambda表达式中无需像传统写法那样声明参数和返回值类型,它会根据你的上下文通过类型推导你实现的是哪一个接口,从而跟具这个接口的定义知道你变量的类型。这也是为什么函数式接口只能声明一个方法的原因。
增加Lambda的目的,其实就是为了支持函数式编程,而为了支持Lambda表达式,才有了函数式接口。
另外,为了在面对大型数据集合时,为了能够更加高效的开发,编写的代码更加易于维护,更加容易运行在多核CPU上,java在语言层面增加了Lambda表达式。
语法特征
(parameters) -> expression
(parameters) -> { statements; }
是lambda表达式的重要特征:
方法的引用让你可以重复使用现有的方法定义,并像lambda一样传递他们,在一些情况下,比起使用lambda表达式,它们似乎更易读,感觉也更自然。
语法:
类::静态方法名
指向静态方法的方法引用,例如Integer的compare方法 ,可以写成Integer::compare
Comparator<Integer> c = Integer::compare;
类::实例方法名
指向任意类型实例方法的方法引用,例如String的equals方法,写成String::equals
BiPredicate<String, String> b = String::equals;
对象::实例方法名
指向现有对象的实例方法的方法引用,例如System.out的println方法,写成System.out::println
Consumer<String> con1 = System.out::println;
User user = new User();
Consumer<String> con2 = user::setUserName;
构造器的引用:ClassName::new
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用,例如:String::new。
Supplier<String> supplier = String::new;
Java8之前已经存在的函数式接口
函数式接口 | 输入参数 | 输出参数 | 描述 |
---|---|---|---|
Runnable | - | - | 多线程自定义执行逻辑,无返回值 |
Callabler |
- | V | 多线程自定义执行逻辑,有返回值 |
PrivilegedAction |
- | T | |
Comparator |
T、T | int | 比较器,常用语比较和排序 |
FileFilter | File | boolean | 文件过滤器 |
PathMatcher | Path | boolean | 路径匹配器 |
InvocationHandler | Object、Method、Object[] | Object | 动态代理,生成代理对象 |
PropertyChangeListener | PropertyChangeEvent | - | 属性改变监听器 |
ActionListener | ActionEvent | - | 动作监听器 |
ChangeListener | ChangeEvent | - | 变动监听器 |
Java8新增的java.util.function包定义的函数式接口
函数式接口 | 输入参数 | 输出参数 | 描述 |
---|---|---|---|
Supplier |
- | T | 生产接口 指定接口的泛型是什么类型,那么接口的get方法就会生产什么类型的数据 |
IntSupplier | - | int | |
LongSupplier | - | long | |
DoubleSupplier | - | double | |
BooleanSupplier | - | boolean | |
Consumer |
T | - | 消费接口 指定接口的泛型是什么类型,那么接口的accept方法就会消费什么类型的数据 |
IntConsumer | int | - | |
LongConsumer | long | - | |
DoubleConsumer | double | - | |
BiConsumer |
T、U | - | |
ObjIntConsumer |
T、int | - | |
ObjLongConsumer |
T、long | - | |
ObjDoubleConsumer |
T、double | - | |
Predicate |
T | boolean | 判断接口 对接口的泛型类型数据进行判断,接口的test方法就是用来得到判断结果 |
IntPredicate | int | boolean | |
LongPredicate | long | boolean | |
DoublePredicate | double | boolean | |
BiPredicate |
T、U | boolean | |
Function |
T | R | 转换接口 接口的apply方法把T数据类型的数据转换为R数据类型的数据 |
UnaryOperator |
T | T | |
ToIntFunction |
T | int | |
ToLongFunction |
T | long | |
ToDoubleFunction |
T | double | |
IntFunction |
int | R | |
IntToLongFunction | int | long | |
IntToDoubleFunction | int | double | |
IntUnaryOperator | int | int | |
LongFunction |
long | R | |
LongToIntFunction | long | int | |
LongToDoubleFunction | long | double | |
LongUnaryOperator | long | long | |
DoubleFunction |
double | R | |
DoubleToIntFunction | double | int | |
DoubleToLongFunction | double | long | |
DoubleUnaryOperator | double | double | |
BiFunction |
T、U | R | |
ToIntBiFunction |
T、U | int | |
ToLongBiFunction |
T、U | long | |
ToDoubleBiFunction |
T、U | double | |
BinaryOperator |
T、T | T | |
IntBinaryOperator | int、int | int | |
LongBinaryOperator | long、long | long | |
DoubleBinaryOperator | double、double | double |
Supplier
抽象方法
public T get():用来获取一个泛型参数指定类型的对象数据
Supplier< T > 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。
举例:
求数组的最大值最小值
public static void main(String[] args) {
Integer[] arr = {2, 7, 3, 4, 5};
//求最大值
Integer max = get(() -> {
Integer a = arr[0];
for (Integer integer : arr) {
a = integer >= a ? integer : a;
}
return a;
});
System.out.println("max:"+max);
// 求最小值
Integer min = get(() -> {
Integer a = arr[0];
for (Integer integer : arr) {
a = integer <= a ? integer : a;
}
return a;
});
System.out.println("min:"+min);
}
public static <T> T get(Supplier<T> supplier) {
return supplier.get();
}
日志
max:7
min:2
Consumer
抽象方法
public void accept(T t):对给定的参数执行此操作。
默认方法
public default Consumer<T> andThen(Consumer<? super T> after):返回一个组合的 Consumer ,按顺序执行该操作,然后执行 after操作。
Consumer
而其默认方法则是可以把两个Consumer接口组合在一起对数据进行消费,谁写在前边就先消费谁,其调用格式为con1.andThen(con2).accept(s)。
举例:
public static void main(String[] args) {
User user = new User();
user.setId("1");
// 打印用户信息
consumer(user, (u) -> System.out.println(JSON.toJSONString(u)));
// 修改用户信息
consumer(user, (u) -> u.setId("2"));
System.out.println(JSON.toJSONString(user));
}
public static void consumer(User user, Consumer<User> consumer) {
consumer.accept(user);
}
日志
{"id":"1"}
{"id":"2"}
BiConsumer
和Consumer
举例:
public static void main(String[] args) {
User user = new User();
user.setId("1");
// 修改用户ID为指定值
biConsumer(user,"2", (u,id) ->u.setId(id));
System.out.println(JSON.toJSONString(user));
}
public static void biConsumer(User user, String id, BiConsumer<User, String> biConsumer) {
biConsumer.accept(user, id);
}
日志
{"id":"2"}
Predicate
抽象方法
public boolean test(T t):在给定的参数上评估这个谓词。
默认方法
public default Predicate<T> and(Predicate<? super T> other):返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND。
public default Predicate<T> or(Predicate<? super T> other):返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或。
public default Predicate<T> negate():返回表示此谓词的逻辑否定的谓词。
Predicate< T > 接口就是用来对某种数据类型的数据进行判断,接口中的test方法就是用来制定判断规则并把结果返回
其三个默认方法其实就是相对三个逻辑——与、或、非。
举例:
public static void main(String[] args) {
Integer integer = 2;
Predicate<Integer> predicate1 = (i) -> i > 1;
Predicate<Integer> predicate2 = (i) -> i < 2;
Predicate<Integer> predicate3 = (i) -> i < 0;
System.out.println(predicate1.test(integer));
System.out.println(predicate1.and(predicate2).test(integer));
System.out.println(predicate1.negate().or(predicate3).test(integer));
}
日志:
true
false
false
BiPredicate
和Predicate
举例:
public static void main(String[] args) {
Integer integer1 = 2;
Integer integer2 = 2;
BiPredicate<Integer, Integer> predicate1 = (i1, i2) -> i1.compareTo(i2) > 0;
BiPredicate<Integer, Integer> predicate2 = (i1, i2) -> i1.compareTo(i2) == 0;
System.out.println(predicate1.test(integer1, integer2));
System.out.println(predicate1.or(predicate2).test(integer1, integer2));
System.out.println(predicate2.negate().test(integer1, integer2));
}
日志:
false
true
false
Function
抽象方法
public R apply(T t):将此函数应用于给定的参数。
默认方法
public default <V> Function<T,V> andThen(Function<? super R,? extends V> after):返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果。
Function< T , R > 接口被用作转换数据类型的,可调用其apply方法把T数据类型的数据转换为R数据类型的数据,但转换规则需要自定义
另外,其默认方法也是把两个Function接口连接起来,第二个Function接口会把第一个Function接口的输出结果作为输入,也可认为这是链式组合(类似方法的链式调用)
举例:
public static void main(String[] args) {
Integer integer = 2;
Function<Integer, Integer> function1 = (i) -> i+1;
Function<Integer, Integer> function2 = (i) -> i*10;
System.out.println(function1.apply(integer));
System.out.println(function2.apply(integer));
System.out.println(function1.andThen(function2).apply(integer));
}
日志:
3
20
30
BiFunction
和Function
举例:
public static void main(String[] args) {
Integer integer1 = 2;
Integer integer2 = 5;
BiFunction<Integer, Integer, Integer> biFunction = (i1,i2) -> i1+i2;
Function<Integer, Integer> function = (i) -> i*10;
System.out.println(biFunction.apply(integer1, integer2));
System.out.println(biFunction.andThen(function).apply(integer1, integer2));
}
日志:
7
70
参考文章:常用函数式接口的介绍与使用