Java8 之方法引用

Posted 潜龙腾渊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8 之方法引用相关的知识,希望对你有一定的参考价值。

什么是方法引用

什么是方法引用?先来看一下下面的例子

public List<Long> operateStr(List<String> strList, Function<String,Long> funcion){
        List<Long> result = new ArrayList();

        for (String str :
                strList) {
            result.add(funcion.apply(str));
        }
        return result;
    }

这是一个字符串操作的函数,可以传入两个参数一个是包含若干字符串的List列表,另外给出一个对字符串操作的函数。这样就可以对List中的每一个字符串自行给定的操作,得到针对每个字符串的结果。我们可以这样调用。

public void useOperateStr(){
        List<String> strList = Arrays.asList("we","are","family");
        List<Integer> strLenList = operateStr(strList,str->str.length());
    }

这样,就可以获得List中每个字符串的长度,组成新的List。还可以这样调用

public void useOperateStr(){
        List<String> strList = Arrays.asList("we","are","family");
        List<Integer> strLenList = operateStr(strList,str->str.contains("a")?1:0);
    }

这次调用,传递的Lambda做了一个判断,检查每个字符串中是否包含a,如果包含,返回1,不包含返回0,最终得到了一个由若干表示字符串是否包含a,组成的1和0的列表。
对于第一个调用,我们还可以这样写

public void useOperateStr(){
        List<String> strList = Arrays.asList("we","are","family");
        List<Integer> strLenList = operateStr(strList,String::length);
    }

这里,用String::length代替了Lambda表达式,这就是函数引用。
在只是对于单一的方法调用的情况下,可以使用方法引用来代替Lambda表达式,使代码更加易读。
但是,对于第二个调用,由于并不是单一的方法调用,所以不能写成方法引用。
方法引用使用“::”操作符,“::”前是引用的方法存在的类名,后面就是引用的方法名称。编译器自动会判断如何使用引用,如何传递参数等。

方法引用的类型

针对引用的方法类型,可以分为以下几种引用

引用静态方法

调用类的静态方法,例如

List<String> strList = Arrays.asList("we","are","family");
        
strList.forEach(System.out::println);

forEach是Java 8 中新增的遍历方法,传入一个Consumer函数式接口,这里传入经常使用的System.out::println,相当于遍历List中的每个字符串,调用System.out.println打印这个字符串。是不是写起来比之前的遍历要简洁很多,同时也更容易读懂了。

对象方法引用

方法引用还可以用来调用对象的方法,来看下面的例子

class Bottle{
    private int volume;
    public Bottle(int water){
        this.volume = water;
    }
    public void pourWater(){
        this.volume = 0;
        System.out.println("Pour "+volume+"L water");
    }
}

定义了一个瓶子类,通过构造函数设置瓶子中的水,再提供一个方法,将瓶子中的水清空,打印一句倒出多少升水。
接下来,写一个方法来使用这个类

List<Bottle> strList = Arrays.asList(new Bottle(3),new Bottle(4),new Bottle(5));
strList.forEach(Bottle::pourWater);

首先,定义了三个瓶子,分别装了3、4、5升水,将瓶子放到List中。
然后,使用forEach传入瓶子的倒水方法引用,对每个瓶子进行倒水操作。
对比静态方法,这里实际执行时,应该是这样的代码
bottle.pourWater();
编译器在运行时会自动检查应该用什么样的调用方式来匹配方法引用。

构造方法引用

类的构造方法比较特殊,不通过方法名称来引用,而是通过new来引用。将构造函数作为参数,也就意味着向函数中传递创建对象的方法。

public class TestMethodRef {

    private Object createObj(Supplier<Abc> supplier){
        return supplier.get();
    }


    public static void main(String[] args) {
        TestMethodRef testMethodRef = new TestMethodRef();
        testMethodRef.createObj(Abc::new);
    }
}

这里通过createObj函数,创建了一个Abc类的实例对象。整个过程看起来很繁琐,完全没有单独使用new来的方便。但是,通过构造方法的引用,我们还可以实现类似工厂的方法。

    private static Map<String,Supplier> mapCreator = new HashMap<>();
    {
        mapCreator.put("String",String::new);
        mapCreator.put("List", ArrayList::new);
    }
    
    public static void main(String[] args) {
        String str = (String)mapCreator.get("String").get();
    }

直接通过给定的字符串生成对象,是不是很神奇。

以上是关于Java8 之方法引用的主要内容,如果未能解决你的问题,请参考以下文章

Java8 之 lambda 表达式方法引用函数式接口默认方式静态方法

Android 使用Java8新特性之"方法引用"

Java8之方法引用

Java8 之方法引用

java8新特性之方法引用和构造器引用

乐字节Java8核心特性实战之四:方法引用