传递Lambda表达式的替代方案

什么是方法引用?

方法引用是Java8的新特性,是传递Lambda表达式的替代方案,没学过Lambda表达式的,建议先看看博主的另一篇博客Lambda表达式

为什么要使用方法引用?

在回答这个问题之前我们先看个例子。

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        integerStream.map(x -> Integer.valueOf(x)).filter(x -> x > 3).forEach(x -> System.out.print(x));
    }
}

在上面的代码中,我们一共使用了三次Lambda表达式,第一个表达式将x转化为Integer类型并返回,而第二个和第三个表达式则没有对x做任何操作,只是将x作为输出语句的实参传递到输出语句中。这种不对参数进行任何操作,仅作为传输媒介的Lambda表达式,就叫传递Lambda表达式。
这类表达式可以正常运行,只是其语法对于当前这个任务而来说太过复杂。我们就开始思考,如何简化这类表达式语法?
针对这个问题,Java8给出了新的解决方案——方法引用

如何使用方法引用

非构造器方法语法:
	(1)方法的所有者对象::方法名(当参数作为被调用方法的实参时)
	(2)参数所属类名::方法名(参数作为目标,即被调用方法是参数自身的方法)

非当前类的成员、静态方法的调用

以例子中的代码的代码为例,print方法属于System.out方法创建的PrintStream对象,所以该方法引用为System.out::print,而parseInt方法属于Integer的字节码对象,所以其方法引用为Integer::parseInt。
使用方法引用调用print和parseInt方法:

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        integerStream.map(Integer::valueOf).filter(x -> x > 3).forEach(System.out::print);
    }
}

可以很明显的看到,使用方法引用后,代码逻辑相对之前简单了很多。
上面是将参数作为非当前类的成员、静态方法的实参传递到该方法时的引用语法。
我们再对代码做个扩展,要求在流中过滤小于3的元素后,再次将Integer元素转为int类型,我们知道Integer的成员方法intValue可以实现该需求,此时可以使用方法引用的第二种语法,我们知道流中参数都是Integer对象,所以方法引用为Integer::intValue。

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        integerStream.map(Integer::valueOf).filter(x -> x > 3).map(Integer::intValue).forEach(System.out::print);
    }
}

当前类成员、静态方法的调用

方法引用调用本类中的成员及静态方法与调用其他类类似,
以当前类为例,在类中添加一个myPrint的成员方法,一个myParseInt的静态方法。
myPrint属于Main对象所有,所以是其方法引用为new Main()::myPrint,而myParseInt属于Main字节码对象所有,所以是其方法引用为Main::myParseInt。
使用方法引用调用myPrint和myParseInt。

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        integerStream.map(Main::myParseInt).filter(x -> x > 3).forEach(new Main()::myPrint);
    }

    public void myPrint(Integer msg){
        System.out.print(msg);
    }

    public static int myParseInt(String numStr){
        return Integer.valueOf(numStr).intValue();
    }
}

这里解释一下,为什么我不直接用this而是使用new关键字重新实例化一个对象。
因为在类加载时对象的实例化是一定晚于静态方法加载的,所以this关键字不能在被static修饰的方法中使用。
那么在什么情况下可以使用this进行方法引用呢?
将以上代码重构,将对流的操作提取到另一个类StreamUtil中。

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        new StreamUtil().myStream(integerStream);
    }

}
class StreamUtil{
    void myStream(Stream stream){
        stream.map(StreamUtil::myParseInt).filter(x -> ((Integer) x).intValue() > 3).forEach(this::myPrint);
    }


    public void myPrint(Object msg){
        System.out.print((Integer) msg);
    }

    public static Integer myParseInt(Object numStr){
        return Integer.valueOf((String)numStr);
    }
}

在子类中调用当前类父类方法与在子类中调用成员方法相似,只是将this换为super。

类构造函数的调用

语法:
	类名::new

将非当前类的成员、静态方法的调用中的代码做个调整,不在进行输出,而是将其保存在ArrayList集合中。

public class Main {
    public static void main(String[] args){
        Stream<String> integerStream = Stream.of("1,2,3,4,5,6".split(","));
        ArrayList<Integer> collect = integerStream.map(Integer::valueOf).filter(x -> x > 3).collect(Collectors.toCollection(ArrayList::new));
        System.out.println(collect);
    }

模糊性与方法引用

至此,使用方法引用的使用就正式介绍完毕,但使用方法引用时应该避免调用指向性不明确的方法,例如Integer的toString方法。

public static String toString(int i) {
    if (i == Integer.MIN_VALUE)
        return "-2147483648";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}
public static String toString(int i, int radix) {
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
        radix = 10;

    /* Use the faster version */
    if (radix == 10) {
        return toString(i);
    }

    char buf[] = new char[33];
    boolean negative = (i < 0);
    int charPos = 32;

    if (!negative) {
        i = -i;
    }

    while (i <= -radix) {
        buf[charPos--] = digits[-(i % radix)];
        i = i / radix;
    }
    buf[charPos] = digits[-i];

    if (negative) {
        buf[--charPos] = '-';
    }

    return new String(buf, charPos, (33 - charPos));
}
public String toString() {
    return toString(value);
}

以上三个方法都是Integer中的toString方法,第一个和第二个是Integer中自定义的两个静态方法,第三个是重写的Object中的toString方法。
所以此时Integer中存在三个toString方法,如果此时通过方法调用,例如使用Integer::toString进行方法调用,此时能被调用到的方法有两个,一个是Integer类中一个参数的静态方法,另一个是Integer对象的成员toString方法,此时JVM无法推断出到底调用的哪个方法。这就是方法引用的模糊性。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早川不爱吃香菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值