美文网首页
Java 8 函数式编程

Java 8 函数式编程

作者: BitterOutsider | 来源:发表于2020-11-27 22:18 被阅读0次

为什么需要函数式编程?

从一个例子说起,我们要过滤用户中id为偶数的用户,或是过滤姓“张“的用户。我们定义filter函数,作为公共过滤函数(复用代码)。该函数接受一个UserPredicate<T>作为参数。其中Predicate<T>是一个@FunctionalInterface接口,Predicate<T>中有个test方法,接受一个T类型对象,并返回一个布尔值。在 Java 8 以前,我们只能使用如下的匿名内部类进行代码的编写,可以看出虽然代码已经进行了一定程度复用,但还是挺麻烦和啰嗦的。

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;
    }

    // 过滤ID为偶数的用户
    public static List<User> filterUsersWithEvenId(List<User> users) {
        return filter(users, new Predicate<User>() {
            @Override
            public boolean test(User user) {
                return user.id % 2 == 0;
            }
        });
    }

    // 过滤姓张的用户
    public static List<User> filterZhangUsers(List<User> users) {
        return filter(users, new Predicate<User>() {
            @Override
            public boolean test(User user) {
                return user.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;
    }
}

所谓函数就是完成x到y的映射x->y。同样的,我们有一个用户映射到一个布尔值User->boolean这样的映射关系,任何满足这个函数定义的东西Java都可以将它转化为一个函数接口@FunctionalInterface(在这个上下文中我们指的是Predicate<User>)。于是在 Java 8 以后,我们有了几种更简单的写法。

对于Predicate<User>,我们需要满足这样的映射关系:User->boolean
lamda表达式: User -> boolean
静态方法引用:static boolean xxx(User user) { }
实例方法引用:boolean xxx() { }

lamba表达式:

public static List<User> filterZhangUsers(List<User> users) {
    return filter(users, user -> user.name.startsWith("张"));
}

静态方法引用(相较于lamda表达式,方法引用有名字,能更好的描述自己在干什么。所以在lamda表达式超过两行时,一般建议使用方法引用):

static boolean userWithEvenId(User user) {
    return user.id % 2 == 0;
}

public static List<User> filterUsersWithEvenId(List<User> users) {
    return filter(users, User::userWithEvenId);
}

实例方法引用(这是因为实例方法的参数中有一个隐含的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 boolean userWithEvenId() {
        return id % 2 == 0;
    }

    // 过滤ID为偶数的用户
    public static List<User> filterUsersWithEvenId(List<User> users) {
        return filter(users, User::userWithEvenId);
    }

    //……
}
  • 函数式编程给我们带了了什么好处?
    • 减少工作量,大大简化代码
    • 提升效率
    • 减少bug

Java 函数接口

记住一句话,任何只含有一个抽象方法的接口都可以被自动转化成函数接口@FunctionalInterface。java 8 以后接口可以包含defalt方法。那什么是抽象方法呢?答案是没有方法体的方法。甚至我们可以自己定义Predicate,替换掉上文中的Predicate,也可以正常工作。

interface 阿猫阿狗 {
  boolean 吃骨头(User user)
}

java.util.function包中,有如下一些比较常用的函数接口(其中每一个都还有平行的三套,这里就不列出了):

  • Consumer<T>将T类型的对象映射为虚空T->void,比如List中的forEach方法。
  • Function<T,R>将T类型对象映射为R类型对象T->R
  • Supplier<T>可以看作是Consumer<T>的逆操作虚空->T

Comparator

Collections.sort(Collection, Comparator)的第二参数就收一个Comparator函数接口。现在有这么一个需求:将用户按年龄从大大小,然后按他们的钱按从小到大排序。如果用匿名内部类,我们有这样的写法:

public class Main {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        User user1 = new User(1, new BigDecimal(5000));
        User user2 = new User(1, new BigDecimal(2000));
        User user3 = new User(5, new BigDecimal(1));
        users.add(user1);
        users.add(user2);
        users.add(user3);

        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                if (o1.age > o2.age) {
                    return -1;
                } else if (o1.age < o2.age) {
                    return 1;
                }
                if (o1.money.compareTo(o2.money) > 0) {
                    return 1;
                } else {
                    return -1;
                }
            }
        });
    }
}

或是使用lamda表达式:

Collections.sort(users, (o1, o2) -> {
    if (o1.age > o2.age) {
        return -1;
    } else if (o1.age < o2.age) {
        return 1;
    }
    if (o1.money.compareTo(o2.money) > 0) {
        return 1;
    } else {
        return -1;
    }
});

Comparator.comparing方法接受一个Function<T,R>函数接口。返回一个Comparator。由此可以看出函数式是多么简洁且优雅。

Collections.sort(users, Comparator.comparing(User::getAge).reversed().thenComparing(User::getMoney));

相关文章

网友评论

      本文标题:Java 8 函数式编程

      本文链接:https://www.haomeiwen.com/subject/mbdfwktx.html