- 初识函数式接口
先介绍一下函数式接口,函数式接口就是有且只有一个抽象方法的接口(可以有多个非抽象方法),常见的函数式接口有Runnable,Comparable等
@FunctionalInterface
public interface Runnable {
void run();
}
public interface Comparable<T> {
int compareTo(T var1);
}
jdk1.8以后增加了java.util.function包下的常用函数式接口(Function、Consumer、Predicate、Supplier、BiFunction、IntBinaryOperator)以及它们的衍生函数式接口。
public interface Consumer<T> {
void accept(T var1);
}
public interface Function<T, R> {
R apply(T var1);
}
public interface Predicate<T> {
boolean test(T var1);
}
public interface Supplier<T> {
T get();
}
public interface BiFunction<T, U, R> {
R apply(T var1, U var2);
}
public interface IntBinaryOperator {
int applyAsInt(int var1, int var2);
}
上面的函数式接口都用了泛型,区别在于方法签名不同。因此,本文只使用前两个函数式接口来举例,其他的原理相似,首先以Function举例:
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
//第一种写法
System.out.println(functionInterface.functionTest(FunctionClient::clientPrint));
//第二种写法
System.out.println((String) functionInterface.functionTest(functionClient -> functionClient.clientPrint()));
}
}
class FunctionInterface {
FunctionClient functionClient = new FunctionClient();
<R> R functionTest(Function<FunctionClient, R> function) {
return function.apply(functionClient);
}
}
这里用的函数式接口是Function,对应apply方法,FunctionalInterface类中的FunctionTest用来模拟sdk开发者给软件开发者的接口,该接口的入参是Function,出参是R:
<R> R functionTest(Function<FunctionClient, R> function) {
return function.apply(functionClient);
}
在function对象调用apply方法时,实际上是执行了箭头后面的代码,也就是functionClient.clientPrint()。functionTest的入参就是表达式"functionClient -> functionClient.clientPrint())",箭头右侧执行的类型也就是函数式接口的返回值类型,由于functionClient.clientPrint()返回的是String,所以这里的R也特指String类型。
除了上面特指的FunctionClient对象,是否也可以使用普通类型来代替箭头左侧的FunctionClient类?
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
Function<String, String> function = "This is left param." -> new FunctionClient().clientPrint();//报错行
System.out.println(functionInterface.functionTest(function));
}
}
class FunctionInterface {
<R> R functionTest(Function<String, R> function) {
return function.apply("This is FunctionalInterface");
}
}
上面的代码会编译失败,原因在于Function无法接受String类型,以及其他常量。由于箭头左边代表的是一个方法的入参,那么这个入参对于该方法来说只能是变量,因此直接定义成常量会报错。
那么如何表示无参的场景嘞?只需要在箭头左边定义一个未声明的变量即可:
Function<String, String> function = s -> new FunctionClient().clientPrint();
对于函数式接口的赋值,箭头左侧只需要传入对应个数的非赋值的变量名即可,如果没有入参,直接使用()即可。
如果只有返回值,没有入参,可以使用Supplier接口:
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
Supplier<String> function = () -> new FunctionClient().clientPrint();
System.out.println(functionInterface.functionTest(function));
}
}
class FunctionInterface {
<R> R functionTest(Supplier<R> function) {
return function.get();
}
}
- 函数式编程的优点是什么?
简化代码,比如:
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Runnable");
}
};
//第一次简化,函数式编程(语句lambda形式)
Runnable runnable2 = () -> {
System.out.println("Runnable");
};
//第二次简化,函数式编程(表达式lambda形式)
Runnable runnable3 = () -> System.out.println("Runnable");
又比如:
Consumer<Integer> consumer = i -> System.out.println(i);
List<Integer> arrayList = new ArrayList<>(){
{
add(1);
add(2);
add(4);
}
};
arrayList.forEach(consumer);
- lambda、函数式编程和回调的关系
1.lambda是函数式接口的实现,可以理解为语法糖。
2.回调对于接口中的方法数量没有限制,但是函数式接口只能指定一个抽象方法。
3.函数式编程和回调的方法定义都在调用方,两者的输入都是接口,只是其中一个是普通接口,另一个是函数式接口。
4.函数式编程和回调的方法都在被调用方执行,前者执行对应的唯一方法,比如apply,而后者可能对应多个方法,执行对应的方法即可。
5.两者在调用方都可以使用lambda的方式定义方法实现。
6.函数式编程实现方式是一种特殊的回调。