方法作为一等公民-Java

                          方法和 Lambda作为一等公民 

方法引用

在Java8以前,如果我们要筛选一个目录中的隐藏文件,我们需要这样做:

    File[] hiddenFiles = new File("").listFiles(new FileFilter() {
        @Override
        public boolean accept(File file) {
            return file.isHidden();
        }
    });

Java8里,我们可以重写成:

File[] hiddenFiles2 = new File("").listFiles(File::isHidden);

:: 这个语法的意思是把这个方法作为值传递给listFiles方法。

 

 

 传递方法

public class Apple {
    String color;
    int weight;

    public Apple(String color, int weight) {
        this.color = color;
        this.weight = weight;
    }

    public static boolean isGreen(Apple apple) {
        return "green".equals(apple.getColor());
    }

    public static boolean isHeavy(Apple apple) {
        return apple.getWeight() > 20;
    }

    public static List<Apple> filterApples(List<Apple> apples, Predicate<Apple> p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

    public static void main(String[] args) {
        List<Apple> apples = List.of(new Apple("green", 21), new Apple("black", 19));
        List<Apple> apples1 = filterApples(apples, Apple::isGreen);
        List<Apple> apples2 = filterApples(apples, Apple::isHeavy);
        apples1.stream().forEach(e -> System.out.println(e));
    }


    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

前面的代码传递了方法Apple::isGreenApple(它接受参数Apple并返回一个 boolean)给 filterApples,后者则希望接受一个Predicate<Apple>参数。谓词(predicate) 在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回true或false。你 在后面会看到,Java 8也会允许你写Function<Apple,Boolean>——在学校学过函数却没学 过谓词的读者对此可能更熟悉,但用Predicate<Apple>是更标准的方式,效率也会更高一 点儿,这避免了把boolean封装在Boolean里面。 

 

实际上并不需要单独写比较过滤方法,可以用这种匿名Lambda形式替代:

List<Apple> apples2 = filterApples(apples, (Apple a) -> a.getWeight() > 20);

但是对于实际开发来讲,这种并非是一种很好的方式,毕竟维护稍微困难了些,如果你写了很长的lambda,还不如老老实实定义方法然后call.

甚至还有一种更简便的方式,直接不需要filterApples()方法:

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p); 

这就是基于流化实现

apples.stream().filter(a -> a.getWeight() > 20).forEach(System.out::println);

 

 

流Strem

        List<Apple> apples = List.of(new Apple("green", 21), new Apple("black", 19));
        Map<String, List<Apple>> groupForApples = new HashMap<>();
        for (Apple apple : apples) {
            if (apple.getWeight() > 20) {
                String color = apple.getColor();
                List<Apple> appleList = groupForApples.get(color);
                if (appleList == null) {
                    groupForApples.put(color, appleList);
                }
                appleList.add(apple);
            }
        }

这段复杂的代码描述的意思很简单,就是将重量大于20的apple按照颜色分组。但描述还是不够清晰的。现在用流转换一下。

        Map<String, List<Apple>> groupForApples2 =
                apples.stream().
                        filter((Apple apple) -> apple.getWeight() > 20).
                        collect(Collectors.groupingBy(Apple::getColor));

emm,很清晰,首先流转化,然后过滤,然后groupBy颜色分组。

总之简单来讲,利用Collection API就是外部迭代,用Stream API就是内部迭代。

 

 并行处理

        Map<String, List<Apple>> groupForApples2 =
                apples.parallelStream().
                        filter((Apple apple) -> apple.getWeight() > 20).
                        collect(Collectors.groupingBy(Apple::getColor));

 

看起来是不是有点像分治,归并的思路。

 

语言特性的添加要考虑的一个很重要的问题是和老版本的兼容。比如我们现在可以对list.sort()这种操作,但是原先是不可以的,现在的处理方法是,我们可以对接口中加入default方法。

这就是我们在List接口中添加的。

default void sort(Comparator<? super E> c) { 
    Collections.sort(this, c);
}

这种default方法,在任何实现类都不需要显示的实现sort。

 

著名的NULL,万能的NULL也是万恶的NULL。如何避免出现NullPointer异常--->Optional<T>

 

 
posted @ 2019-12-29 20:36  zhangyu63  阅读(398)  评论(0编辑  收藏  举报