三种方式实现函数式编程

三种方式实现函数式编程

摘要

\quad 在Java8引入函数式编程以后,不少复杂的代码能够大大的简化,甚至能够简化成一句话。这里就主要经过一个例子,分析使用Lambda表达式、静态方法以及实例方法简化代码的优缺点。express

目录

(点击可直接跳转)
*1.前言
*2.使用Lambda表达式
*3.使用静态方法
*4.使用实例方法
*5.总结

1.前言

\quad 经过这个例子,一方面能够认识到,本身之前写过的代码可读性是多么的差,另外一方面能够知道如何优雅的使用接口。之前使用接口的时候,只知道实现了这个接口,就表明这个类具备某种能力。可是经过这个例子,我看到了接口还能够用在方法中。实现接口的时候,只须要在lambda表达式中提供对应的(输入/返回值)类型便可。
编程

2.使用Lambda表达式

先看下面这段代码:
将map中的k,v值分别用不一样的链接符打印出来
public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }
    }

    public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-" + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-" + value);
        }
    }

    public static void printWithColon(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ":" + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ":" + value);
        }
    }
    
复制代码

看完后发现,里面很是多的重复代码,天然而然的想到,能够将其中的公共代码抽取出来。而且函数是输入两个map,获得一个void,这符合BiConsumer接口设定。因此,能够在抽取的公共方法中实现BiConsumer接口,实现代码复用。
代码以下:bash

public static void printWithConsumer(
         Map<String, String> map1,
         Map<String, String> map2,
         BiConsumer<String, String> consumer) {
     map1.forEach(consumer);
     map2.forEach(consumer);
 }
复制代码

打印方法就能够被简化成一句话了:函数式编程

public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
        printWithConsumer(map1, map2, (s, s2) -> System.out.println(s+","+s2));
    }
复制代码

3.使用静态方法

\quad 一样使用第二个例子,对第二种方法进行简化。函数

public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
        printWithConsumer(map1, map2, RefactorToConsumer::staticPrintWithDash);
    }

    public static void staticPrintWithDash(String s1, String s2) {
        System.out.println(s1 + "-" + s2);
    }
复制代码

\quad定义的静态方法消耗两个String,获得一个void,符合BiCounsumer的约定,因此可使用静态方法引用的方式简化代码。
\quad分析两者优缺点:
1.使用静态方法能够给方法取名字,这样更加直观,更容易被理解。
2.使用静态方法能够在方法中写较为复杂的代码,而Lambda中通常就是一两句话。因此须要较多的代码实现的时候,最好仍是在静态方法中声明。
ui

4.使用实例方法

\quad 为了便于理解,这里使用另一个简单的例子: 对一个User类实现过滤
代码以下:this

public class User {
   
    private final Integer id;
    private final String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }
    
    // 过滤姓张的用户
    public static List<User> filterZhangUsers(List<User> users) {
        List<User> list = new ArrayList<>();
        for (User user:users
             ) {
            if (user.name.startsWith("张")){
                list.add(user);
            }
        }
        return list;
    }
}
复制代码

\quad 为了减小排版,我这里只写了一个过滤器,可是能够想象若是须要过滤姓王的,ID为偶数的User...咱们就能够将其抽取出来,造成一个公共的抽取方法。不管用前面的Lambda表达式也好,用静态方法也能够,实现Predicate接口便可。除此以外,咱们还发现这个断定的过程其实就是User类型转换为boolean类型的过程,而刚好方法就是在User类中定义的,因此没必要将方法定义成static类型,使用实例方法便可。
代码以下:spa

public static List<User> filterZhangUsers(List<User> users) {
       return filter(users,User::filterNameStartWithZhang);
    }
    
    public boolean filterNameStartWithZhang(){
        return this.name.startsWith("张");
    }

    public static List<User> filter(List<User> users, Predicate<User> predicate) {
        List<User> results = new ArrayList<>();
        for (User user : users
        ) {
            if (predicate.test(user)) {
                results.add(user);
            }
        }
        return results;
    }
复制代码

\quad因为 filterNameStartWithZhang()方法是非静态的,在方法形参表中看似是空的,实际上形参是(User this),符合Predicate的从某一类型到boolean的设定。
code

5.总结

总结:
\quad1.将大量的重复代码进行重构,是一件很是有必要的事情。不只有形的减小代码量,并且在无形中减小了出bug的机率,而且大大的增长了代码的可读性。
\quad 2.使用Lambda表达式须要注意,在表达式中引用的变量都须要是effectively final类型的,不然会报
“Variable used in lambda expression should be final or effectively final”。
接口

解决方法:
1.可使用Atomic类型的参数。2.能够将参数声明为全局类型的。
详细参考这篇文章:zhuanlan.zhihu.com/p/82921974

相关文章
相关标签/搜索