函数式编程
面向抽象是编程的一个思想,在开发中我们往往会定义许多的接口,一个接口往往对应多个实现类,有时我们会采用匿名类方式来快速完成接口的实现,代码常常会这样:
public interface ISelfOperate{
public int myAdd(int a,int b);
}
// 自定义求和
public static int calBySelfWay(int a,int b,ISelfOperate iSelfOperate) {
return iSelfOperate.myAdd(a, b);
}
public static void main(String[] args) {
// 匿名类实现
int total = calBySelfWay(1, 2, new ISelfOperate() {
@Override
public int myAdd(int a, int b) {
return a + b;
}
});
System.out.println(total);
}
Java8提供一种新的思路,函数式编程的思想:
@FunctionalInterface
public interface ISelfOperate{
public int myAdd(int a,int b);
}
// 自定义求和
public static int calBySelfWay(int a,int b,ISelfOperate iSelfOperate) {
return iSelfOperate.myAdd(a, b);
}
public static void main(String[] args) {
int total = calBySelfWay(1, 2, (a,b) -> a+b);
System.out.println(total);
}
上面代码接口的实现通过传递 (a,b) -> a+b
来实现自定义的函数功能,这个表达式称作Lambada表达式
,使用Lambada表达式进行编程可以达到更集中在函数行为的编程,简单便捷
Lambada表达式是一种取代匿名类编程的新思路,通过Lambada表达式来直接写出接口的实现部分;
如何声明函数式接口
函数式接口:有且仅有一个抽象方法的接口;常常使用@FunctionalInterface注解标记
@FunctionalInterface
public interface ISelfOperate{
public int myAdd(int a,int b);
}
@FunctionalInterface
注解可以协助编译器进行编译检查接口是否为函数式接口,不是则会提示错误信息;
比如,函数式接口中声明两个抽象方法,则会提示错误。
@FunctionalInterface
public interface ISelfOperate{
public int myAdd(int a,int b);
public int myMulti(int a,int b);
}
声明完函数式接口,就可以使用Lambada表达式进行接口的实现调用。
Lambada表达式的编写规范
- ()中传递与接口中方法参数类型和数量一致的参数,类型可以省略不写,由编译器自行推断
- 方法体内只有一行代码时,可以直接写代码,不用{}包着,代码不需要以;结束;
- 方法体内有多行代码时,必须使用{}包着,{}内代码按照平常书写代码的方式进行书写;
例如:
int total1 = calBySelfWay(1, 2, (a,b) -> a+b);
int total2 = calBySelfWay(1, 2, (int a,int b) -> a+b);
int total3 = calBySelfWay(1, 2, (int a,int b) -> {return a+b;});
可以看出,在这种接口下
(a,b) -> a+b
等同于
(int a,int b) -> {return a+b;}
因此,Lambada表达式的书写要与函数式接口参数和返回值完全一致(数量和类型一致),才能编译通过,当我们使用函数式接口的时候,我们只需要把注意力放在参数和返回值类型上就可以了。
Java8 内置的函数式接口
java.util.function
包下放着Java8函数式接口,常用的四个函数式接口为:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
使用这些内置的函数式接口,就可以来写出符合的Lambada表达式进行编程了。
通过Lambada表达式来创建接口的实例:
Consumer<String> consumer = (str) -> System.out.println(str);
Supplier<String> supplier = () -> Math.random() + "";
Predicate<String> predicate = (str) -> str.length() > 5;
Function<Integer, String> function = (integer) -> "整数" +integer;
等同于匿名类实现:
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
函数式接口跟我们平常自定义的普通接口使用是一样的,只是扩充了对Lambada的支持,使得编程更加方便。在java.util.function
包下提供了许多在这四个函数式接口扩充的接口,具体介绍可以查看官方API和源码,使用方式也类似。
方法引用
方法引用:使用操作符 ::
将方法名和对象或类的名字分隔开来。
当我们书写Lambada表达式时,如果方法实现有JDK现成的方法可以调用,我们就可以采用方法引用来编写更简单的代码,比如:
Consumer<String> consumer = (str) -> System.out.println(str);
可以看到这个函数式接口的实现部分只是简单的一行代码,并且这个System.out.println(str)
也是JDK已存在的方法,可以采用方法引用来进行简写
Consumer<String> consumer = System.out::println;
方法引用是我们通过引用一个已存在的方法来实现函数式接口,因此,方法引用只要我们保证引用的方法跟函数式接口参数和返回值一样就可以了,书写方法引用也无需写参数,只是简单的引用来实现接口。
方法引用支持的三种语法
1.对象::实例方法
2.类::静态方法
3.类::实例方法
// 类::静态方法
BiFunction<Double,Double,Double> binaryOperator =Math::pow;
BiFunction<Double,Double,Double> binaryOperator1 =(a,b) -> Math.pow(a, b);
// 对象::实例方法
Consumer<String> consumer1 = System.out::println;
Consumer<String> consumer = (str) -> System.out.println(str);
// 类::实例方法
BiPredicate<String,String> biPredicate = String::equals;
BiPredicate<String,String> biPredicate1 = (a,b) -> a.equals(b);
类::实例方法
这种写法有点难以理解,满足条件如下:
当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参(或无参数)时:ClassName::methodName
学习Lambada表达式和方法引用后,可以写出更优雅的代码。
网友评论