java函数式编程

java8终于引入了函数式编程,函数式编程即对lambda(λ)表达式的运用,熟练运用λ表达式,我们实现功能的思路会变得更清晰,写起代码来也可以更简捷,下面来认识和学习一下λ

一、认识λ表达式,对于λ表达式,我们看到的使用得最多的就是集合的操作了,下面看几个例子
List<String> list = Arrays.asList("abc", "bcd", "cdef");
List<Integer> lengthList = list.stream().map((item)->item.length()).collect(Collectors.toList());
// 其中(item)->item.length() 为λ表达式
lengthList.forEach((item) -> System.out.println(item));
// 其中(item) -> System.out.println(item) 也为λ表达式

这两个例子中,都是在函数调用的时候,λ表达式作为参数的,一个是stream的map函数,一个是list的forEach函数,我们可以进入函数定义查看一下源码

//map函数
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//forEach函数
default void forEach(Consumer<? super T> action) {
...
}

从形式上我们可以看出来,λ表达式最终被当成一些类型于Function<? super T, ? extends R>,Consumer<? super T>这样的接口的“对象”来处理的,我们再看这两个接口定义(为了方便,先省去了一些代码)

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

我们发现这两个接口都被@FunctionalInterface注解了,根据FunctionalInterface的注解说明,被它注解的接口,只允许有一个非default的接口方法(未实现的方法),然后我们再回看λ表达式发现,(item)->item.length()为Function的对象,(item) -> System.out.println(item)为Consumer的对象,而观察它们的唯一的非default的方法,也就是(item)->item.length()对应R apply(T t), (item) -> System.out.println(item)对应void accept(T t),是否说明我们可以直接把λ表达式赋值给这些呢,我们可以试试

Function<String, Integer> fun1 = (item) -> item.length();
Consumer<String> fun2 = (item) -> System.out.println(item);

我们发现确实可行。

//(item) -> item.length()还可以写成
(String item) -> {
    int length = item.length();
    return length;
}
//函数输入为String类型,输出为int类型,对应于Funcation<String, Integer>的apply方法
//(item) -> System.out.println(item)也可以写成
(String item) -> {
    System.out.println(item);
}
//函数输入为String类型,输出为void类型,对应于Consumer<String>的accept方法

Function可以表示任意只有一个参数,有返回值的函数,类型不限(非void), Consumer可以表示任务只有一个参数,没有返回值的函数,我们可以试一下

public static class FunClass {
    public int len(byte[] attr){
        return attr.length;
    }
    public void doNothing(byte[] attr){
    }
}
FunClass obj = new FunClass();
Function<byte[], Integer> fun3 = (attr) -> obj.len(attr);
Consumer<byte[]> fun4 = (attr) -> obj.doNothing(attr);

测试发现确实可行,我们还看到java.util.function包下面还有许多的函数接口,许多都是被@FunctionalInterface注解的,它们都是能作为一个λ表达式的引用,因此我们可以认为它们都是函数”模板”

二、函数式编程的写法

还可以有不同的写法,这些都是等价的

Consumer<String> fun5 = System.out::println;
Function<byte[], String> fun6 = String::new;
Function<Integer, String> fun7 = String::valueOf;
Supplier<String> fun8 = new Object()::toString;

Consumer<String> fun5 = (item)-> System.out.println(item);
Function<byte[], String> fun6 = (attr)-> new String(attr);
Function<Integer, String> fun7 = (num) -> String.valueOf(num);
Supplier<String> fun8 = () -> new Object().toString();

Consumer<String> fun5 = (String item)-> {System.out.println(item);};
Function<byte[], String> fun6 = (byte[] attr)-> {return new String(attr);};
Function<Integer, String> fun7 = (Integer num) -> {return String.valueOf(num);};
Supplier<String> fun8 = () -> {return new Object().toString();};
三、函数式编程的用法
Consumer<String> fun5 = System.out::println;
fun5.accept("hello world");
//效果跟System.out.println("hello world");一样

既然可以把一个函数赋值给一个引用,这个引用应该也可以作为函数的参数,确实是这样,比如前面提到的forEach, map函数,我们也可以定义函数

public static <T> void fun(Consumer<T> consumer, T obj){
    consumer.accept(obj);
}
//调用方式如下:
fun(System.out::print, "hello world");
fun(new ArrayList<String>()::add, "hello world");
四、自定义函数式编程模板

查看java.util.function包下面的”模板”,我们发现只有一些基础的函数的模板,要是想引用一个有三个或更多参数的函数,现有的模板中似乎就无法满足条件,但是我们可以根据@FunctionalInterface注解来自定义模板

//定义模板
@FunctionalInterface
interface TriFunction<R1, R2, R3, T> {
    T apply(R1 r1, R2 r2, R3 r3);
}
//定义类
public static class FunClass {
    public static String get(String name, int age, boolean test){
        return name + age + test;
    }
    public static <R1, R2, R3, T> T fun(TriFunction<R1, R2, R3, T> func, R1 r1, R2 r2, R3 r3) {
        return func.apply(r1, r2, r3);
    }
}
//函数使用
TriFunction<String, Integer, Boolean, String> triFun = FunClass::get;
triFun.apply("word", 12, false);//相当于调用FunClass.get("word", 12, false);

因此我们可以定义任务的类型的函数模板,然后使用它

五、函数模板与函数表达式对应关系并不绝对
Consumer<String> fun10 = String::new;
Function<String, String> fun12 = String::new;

Function<String, Integer> fun13 = (item) -> item.length();
Consumer<String> fun14 = (item) -> item.length();

Function<String, Integer> fun15 = (String item) -> {item.length();};
Consumer<String> fun16 = (String item) -> {item.length();};

Function<String, Integer> fun15 = (String item) -> {return item.length();};
//但是λ表达式涉及到return 的时候,就没法用Consumer了,Consumer<String> fun16 = (String item) -> {return item.length();};这样会编译报错
六、函数模板与λ表达式关系

Function<R, T> 的方法为 T apply(R r);
对应的 λ表达式为 (r) -> {return t};
Consumer 的方法为 void accept(R r);
对应的 λ表达式为 (r) -> { //do work };

当我们定义的模板接口为 T func(R1 r1, R2 r2, R3 r3);的时候
则对应的λ表达式应为 (r1, r2, r3) ->{return t;};
因此我们可以定义任意的函数模板与λ表达式的对应关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值