前言
现在的 Java 项目大量用到了函数式编程,为了巩固相关知识,写一篇文章记录一下学习心得。
Java 并不是函数式语言,但是从 Java 8 开始的 Lambda 表达式和方法引用允许我们用 Java 语言来进行函数式编程。
通常,传递给方法的数据不同,结果不同,如果我们希望方法在调用的时候行为不同,只需要将代码传递给方法,也就是把方法传递给方法,就可以控制方法的行为,这就是函数式编程其中一个意义所在,下面来看一下 Java 是如何支持函数式编程的。
Lambda 表达式
Lambda 表达式是使用最小可能语法来编写函数,看下面例子:
interface Service {
String print(String name);
}
class Basketball implements Service {
@Override
public String print(String name) {
return name + "在打篮球";
}
}
class People {
Service service;
String name;
People(String name) {
this.name = name;
}
void setService(Service service) {
this.service = service;
}
void process() {
System.out.println(service.print(name));
}
}
public class Test {
public static void main(String[] args) {
People people = new People("小明");
Service service = new Service() {
@Override
public String print(String name) {
return name + "在打篮球";
}
}; // 1
people.setService(service);
people.process();
service = new Basketball(); // 2
people.setService(service);
people.process();
service = (name -> name + "在打篮球"); // 3
people.setService(service);
people.process();
service = (a -> a + "在踢足球"); // 4
people.setService(service);
people.process();
}
}
复制代码
输出结果:
小明在打篮球
小明在打篮球
小明在打篮球
小明在踢足球
复制代码
上面的代码用三种不同的方式实现了同一个功能,目的是为了让 People
类型的对象调用自身的 process()
方法来打印输出相关信息。
- 创建一个匿名内部类,存在冗余代码。
- 常规做法,麻烦。
- Lambda 表达式,用
->
来分割参数和函数体,箭头左边是参数,箭头右边是函数体,当函数体只有一行时,表达式的返回值就是函数的返回值,这实现了与匿名内部类、定义类相同的效果,但代码少很多。 - 这里可以看到
->
左边的参数可以任意命名,只要 Lambda 函数体和返回值与Service
接口中的方法体一一对应即可,原本小明的行为是打篮球,现在我把踢足球的方法传给了他,他就在踢足球了,这里我们传递的是方法,也就是把方法当作一个对象来进行传递,妙啊!
当然 Lambda 表达式还有其他语法变体:
interface Description {
String brief();
}
interface Body {
String detailed(String head);
}
interface Multi {
String twoArg(String head, Double d);
}
public class LambdaExpressions {
static Body bod = h -> h + " No Parens!"; // 1
static Body bod2 = (h) -> h + " More details"; // 2
static Description desc = () -> "Short info"; // 3
static Multi mult = (h, n) -> h + n; // 4
static Description moreLines = () -> { // 5
System.out.println("moreLines()");
return "from moreLines()";
};
public static void main(String[] args) {
System.out.println(bod.detailed("Oh!"));
System.out.println(bod2.detailed("Hi!"));
System.out.println(desc.brief());
System.out.println(mult.twoArg("Pi! ", 3.14159));
System.out.println(moreLines.brief());
}
}
复制代码
输出结果:
Oh! No Parens!
Hi! More details
Short info
Pi! 3.14159
moreLines()
from moreLines()
复制代码
- 当只用一个参数,可以不需要括号
()
然而,这是一个特例。 - 正常情况使用括号
()
包裹参数。 为了保持一致性,也可以使用括号()
包裹单个参数,虽然这种情况并不常见。 - 如果没有参数,则必须使用括号
()
表示空参数列表。 - 对于多个参数,将参数列表放在括号
()
中。到目前为止,所有 Lambda 表达式方法体都是单行。 该表达式的结果自动成为 Lambda 表达式的返回值,在此处使用 return 关键字是非法的。 这是 Lambda 表达式缩写用于描述功能的语法的另一种方式。 - 如果在 Lambda 表达式中确实需要多行,则必须将这些行放在花括号中。 在这种情况下,就需要使用 return。
方法引用
方法引用语法:类名或对象名::方法名称。
interface Service { // 1
String print(String name);
}
class Swim {
public String swim(String name) { // 2
return name + "在游泳";
}
}
class Jijian {
public static String jijian(String name) { // 3
return name + "在击剑";
}
}
public class Test {
public static void main(String[] args) {
Swim swim = new Swim();
Service service = swim::swim; // 4
System.out.println(service.print("小明"));
service = Jijian::jijian; // 5
System.out.println(service.print("小明"));
}
}
复制代码
输出结果:
小明在游泳
小明在击剑
复制代码
- 从单一接口方法开始(接口中只能有一个抽象方法)。
swim()
的签名(参数类型和返回类型)符合Service
的print()
签名。jijian()
也符合。- 将
Swim
对象的方法引用赋值给Service
,它没有swim()
方法,而是print()
方法,但是 Java 接受这样看似奇怪的赋值,因为方法引用符合函数的签名。 - 静态方法直接用类名来引用即可。
总结:OO(object oriented,面向对象) 是抽象数据,FP(functional programming,函数式编程) 是抽象行为。
Java 函数式编程远不止这些,本文参考了《On Java 8》,是《Java编程思想》的作者写的,详情可参考下方链接:
github.com/kyiree/Ling…
近期评论