美文网首页Java 之旅
中级10 - Java 8函数式编程

中级10 - Java 8函数式编程

作者: 晓风残月1994 | 来源:发表于2020-06-02 17:27 被阅读0次

    Java 8是Java历史上的一个里程碑。它引入的函数是编程范式大大减少了开发者的负担,这相当于延长了开发者的生命。

    • lambda表达式
    • 方法引用
    • 接口的默认方法
    image.pngimage.png

    1. lambda 表达式

    y = f(x)

    在一个接口中,抽象方法只有一个时,该接口可以被认为是函数接口,任何符合该抽象方法输入输出定义的方法都可以在需要时自动转换为该函数接口。
    Java 中有很多函数接口,在 java.util.function 中。Java 8中 Predicate 接口有五个方法,但只有一个 test 方法是抽象方法,所以其是函数接口。

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

    filter 方法需要传入一个 Predicate 接口的实现,而 Predicate 是一个函数接口,把输入的对象映射成布尔值作为输出,任何满足这种函数映射关系的都可以自动转化为该 Predicate 函数接口。

    有三种形式可能满足 Predicate 接口。

    lambda 表达式:

    // user -> user.id % 2 == 0
    // 只有一个参数一条语句时不需要加括号
        
    public static List<User> filterUsersWithEvenId(List<User> users) {
        return filter(users, user -> user.id % 2 == 0);
    }
    

    lambda 表达式更简洁,但语句超出一行的话,使用方法引用更好,毕竟可以起名字,也可以不失优雅地多写几行。

    静态方法引用:

    private static boolean userWithEvenId(User user) {
        return user.getId() % 2 == 0;
    }
    
    public static List<User> filterUsersWithEvenId(List<User> users) {
        return filter(users, User::userWithEvenId);
    }
    

    实例方法引用:
    因为每个实例方法,编译器编译时都会偷偷为方法传入 this 参数,所以下面的实例方法刚好也是满足 User -> boolean 这种映射关系,所以也能自动转换为 Predicate 接口。

    public boolean isWang(/*User this*/) {
        return this.name.startsWith("王");
    }
    public static List<User> filterUsersWithEvenId(List<User> users) {
        return filter(users, User::isWang);
    }
    

    开头说了任何只包含一个抽象方法的接口都可以被认为是函数接口,所以上面的例子即使不使用 Predicate 接口也无所谓,自己实现一个满足 User -> boolean 映射关系的函数接口即可,连泛型都不用。然后替换 filter 方法,上面调用 filter 方法时传入的三种形式,依然满足 filter 第二个参数的要求,即本质满足 我是个随便的接口 中 User -> boolean 这种关系映射即可:

    interface 我是个随便的接口 {
        boolean 行吗(User user);
    }
    
    public static List<User> filter(List<User> users, 我是个随便的接口<User> predicate) {
        List<User> results = new ArrayList<>();
        for (User user : users) {
            if (predicate.行吗(user)) {
                results.add(user);
            }
        }
        return results;
    }
    

    2. 方法引用

    刚才已经提到了方法引用,相比于 lambda 表达式,好处:

    • 具名,可以自我解释
    • 方法可以任意复杂

    3. Function 函数接口

    import java.util.function.Function;
    
    public class User {
        Integer id;
        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 String map(User user, Function<User, String> function) {
            return function.apply(user);
        }
        
        // 满足 User -> String 映射关系的都可以
        public static void main(String[] args) {
            map(new User(2, "wang"), user -> "");
            map(new User(2, "wang"), user -> user.getName());
            map(new User(2, "wang"), User::getName);
        }
    }
    

    4. Consumer 和 Supplier 函数接口

    二者相反:

    • Consumer:void accept(T t)
    • Supplier:T get();

    5. Comparator

    • comparing()
    • reversed()
    • thenComparing()

    看一个之前在讲接口和抽象类时的例子,现在使用 java8 函数式改进只需一行代码:

    import java.io.IOException;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    public class Point implements Comparable<Point> {
    
        private final int x;
        private final int y;
        // 代表笛卡尔坐标系中的一个点
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        public int getX() {
            return x;
        }
    
        public int getY() {
            return y;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
    
            Point point = (Point) o;
    
            if (x != point.x) {
                return false;
            }
            return y == point.y;
        }
    
        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }
    
        @Override
        public String toString() {
            return String.format("(%d,%d)", x, y);
        }
    
        // 重点是这里实现了 Comparable 接口
        @Override
        public int compareTo(Point that) {
            if (this.x < that.x) {
                return -1;
            } else if (this.x > that.x) {
                return 1;
            } else if (this.x == this.x) {
                if (this.y < that.y) {
                    return -1;
                } else if (this.y > that.y) {
                    return 1;
                }
            }
            return 0;
        }
    
        // 按照先x再y,从小到大的顺序排序
        // 例如排序后的结果应该是 (-1, 1) (1, -1) (2, -1) (2, 0) (2, 1)
        public static List<Point> sort(List<Point> points) {
            Collections.sort(points); // 这里将集合传入方法中
            return points;
        }
    
        public static void main(String[] args) throws IOException {
            List<Point> points =
                    Arrays.asList(
                            new Point(2, 0),
                            new Point(-1, 1),
                            new Point(1, -1),
                            new Point(2, 1),
                            new Point(2, -1));
            System.out.println(Point.sort(points));
        }
        
        // 使用java8函数式
        Collections.sort(points, 
                         Comparator.comparing(Point::getX).reversed()
                         .thenComparing(Point::getY);
    }
    

    相关文章

      网友评论

        本文标题:中级10 - Java 8函数式编程

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