🥂 Java中的函数式编程
面向对象编程思想和函数编程思想
通过面向对象设计一个程序
有这样的一个需求,需要设计一个 程序员使用字符处理器来处理文本的程序.
根据面向对象的设计思想,我们需要设计这样两个类.
- 程序员类
- 字符处理器类
考虑到可能用到不同的字符处理器,所以设计一个字符处理器接口,不同的字符处理器继承该接口以供程序员在不同的业务场景下使用.
得到的类图如下:
实现上述代码:
创建一个 Programmer程序员类,提供了工作的方法,该方法可以使用 字符处理器来处理一个字符
定义一个 StringProcessor字符处理器接口,他提供一个 apply方法
创建一个 AppendStringProcessor类, 继承 StringProcessor实现 append方法
/**程序员*/
public class Programmer{
/**工作方法,通过使用字符处理器的apply()方法处理字符*/
public String work(StringProcessor sp,String str){
retrun sp.apply(str);
}
}
/**字符处理器*/
public interface StringProcessor{
String apply(String str);
}
/** 对字符增加前缀的处理器 */
public class AppendStringProcessor implements StringProcessor{
@Override
String append(String str){
return "斯密" + str;
}
}
面向对象的语言强调,万物皆对象,我们必须通过调用合适的对象的合适的方法来完成一件事,所以该程序运行过程为
创建程序员对象
创建字符处理器对象
在程序员的work方法中,我们调用了字符处理器的apply 方法 完成了对字符的处理
//程序员对象
Programer programer = new Programer();
//字符处理器对象
StringProcessor stringProcessor = new AppedStringProcessor();
//调用对象的方法
String result = programer.work(stringProcessor,"马赛");
上述代码是通过面向对象的思想来完成的,我们通过字符处理器类,创建字符处理器对象,调用该对象的方法来完成编码,但是抛开面向对象,我需要的只是处理字符串的行为,也就是实现类的方法体部分,并不需要字符处理器的对象
lambda 解决的就是在一些情况下,面向对象代码的笨重和不灵活.他可以让我们关注于方法,而不需要创建并使用一个拥有该方法的对象.
接下来对代码进行改造,一步步修改为通过** lambda** 完成功能
1. 通过匿名类代替创建 AppendStringProcessor 类
StringProcessor stringProcessor = new StringProcessor() {
@Override
public String append(String str) {
return "斯密" + str;
}
};
使用匿名类后,便不需要创建实现接口的类
2. 匿名类向lambda进行演变
- 去掉多余的部分,只保留 方法参数和 方法体,参数和 **方法 **之间加上符号 -> .
StringProcessor stringProcessor = (String str) -> {
return "斯密" + str ;
};
- 把 retrun 和 **{ } **去掉
StringProcessor stringProcessor = (String str) -> "斯密" + str;
- 去掉参数类型,当参数只有一个时,还可以把 ( ) 去掉
StringProcessor stringProcessor = str -> "斯密" + str;
这个时候,代码已经由匿名类,演变为 lambda 了.
3. 通过java.util.function提供的函数式接口,代替自己创建 StringProcessor 接口
创建 **StringProcessor **接口是为了 提供一个接受一个 **String **类型的参数,返回一个 **String **类型的结果的方法, **java.util.funciton **包中已经提供的各种各样的函数式接口,可以直接使用.
**java.util.function **包下函数式接口的行为和功能
选择符合条件的函数式接口 java.util.function.Function<T,R>
Function<String,String> stringProcessor = str -> "斯密" + str;
这个时候代码变成了如下
//程序员对象
Programer programer = new Programer();
//调用对象的方法
String result = programer.work(str -> "斯密" + str , "马赛");
函数式编程中函数作为一等公民
第一等公民,指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
把处理字符的方法当参数传递给了 **work **方法,便是函数式编程
函数式编程在开发中用处
ArrayList 是如何使用lambad实现循环的
//调用ArrayList的forEach方法
new ArrayList<>().forEach(ele -> {
System.out.println(ele);
});
//forEach的源码
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
int expectedModCount = this.modCount;
Object[] es = this.elementData;
int size = this.size;
for(int i = 0; this.modCount == expectedModCount && i < size; ++i) {
action.accept(elementAt(es, i));
}
}
forEach( ) 方法中的参数,便是处理集合中元素的方法,这里同样是把方法作为参数传递.
循环集合的目的是为了处理元素,我们只需要把如何处理元素的方法作为参数传递给它,而不是面向对象思想中向它提供一个拥有处理元素方法的对象.这就是函数式编程之所以可以让程序变得简单的原因.
方法引用
方法引用是 lambda 的语法糖.当发生如下情况时
new ArrayList<String>().forEach(ele -> {
System.out.println(ele);
});
在这个函数中,方法体仅仅是调用了另一个方法,那么可以直接使用这个方法来作为参数传递.
new ArrayList<String>().forEach(System.out::println);
使用了System.out对象的println方法来代替了自己创建函数