Java 8 方法引用(Method Reference)详解

本文详述了Java 8中的方法引用,包括引用静态方法、已存在对象的实例方法、特定类型任意对象的实例方法及构造器。通过实例展示了它们的使用,并对比了方法引用与lambda表达式的差异,强调了方法引用在代码可读性上的优势。
部署运行你感兴趣的模型镜像

概述

Java 8 引入了新特性lambda表达式。Lambda表达式是一种不隶属于任何类的匿名方法。其可以使用在函数接口(functional interface)定义的变量。函数接口是一种只包含一个抽象方法(abstract method),同时可以包含多个默认方法(default method)和静态方法(static method)的接口。因此,lambda表达式也可以看做另一种形式的匿名类。

方法引用是一个与Lambda表达式相关的重要特性。可以同lambda一样,将方法引用传递给函数接口。方法引用的形式如:

Object::methodName

双引号::用来表示引用一个方法。

方法引用分类

有四种类型的方法引用。具体如下

类型方法引用语法Lambda表达式语法
引用静态方法(Reference to a static method )ClassName::staticMethodName(args) -> ClassName.staticMethodName(args)
对已存在对象的实例方法的引用(Reference to an instance method of an existing object)object::instanceMethodName(args) -> object.instanceMethodName(args)
引用特定类型的任意对象的实例方法(Reference to an instance method of an arbitrary object of a particular type)ClassName::instanceMethodName(arg0,rest) -> arg0.instanceMethodName(rest) Note: arg0 is of type ClassName
引用构造器(Reference to a constructor)ClassName::new(args) -> new ClassName(args)

其中,引用特定类型的任意对象的实例方法可能相对难以理解,下面本文将通过实例详细解读这四种方法引用类型。

引用静态方法

下述代码通过定义一个函数接口interface IntPredicate,该接口有一个抽象方法check()。check()方法接受一个类型为int参数,且返回值为类型boolean。IntPredicatesChecker类定义了两个静态方法。MethodRef类则有一个numCheck()方法,第一个参数为函数接口IntPredicate类型,第二个参数为int。 本实例通过numCheck的第一个参数分别接受lambda和静态方法引用来说明静态方法引用的使用。

public class MethodRef {
    public MethodRef() {
    }

    // 该方法将函数接口作为第一个参数
    public boolean numCheck(IntPredicate p, int n) {
        return p.check(n);
    }

    public static void main(String[] args) {
        MethodRef demo = new MethodRef();
        int num = 11;
        // 使用lambda 表达式检查number是否偶数
        IntPredicate lb1 = number -> (number % 2) == 0;
        boolean result = demo.numCheck(lb1, num);
        System.out.println("Using lambda expression: " + num + " is even: " + result);

        // IntPredicatesChecker 的isEven方法作为静态方法引用传递给numCheck()
        result = demo.numCheck(IntPredicatesChecker::isEven, num);
        System.out.println("Using static method reference: " + num + " is even: " + result);

        // 使用lambda表达式检查number是否为正数
        IntPredicate lb2 = number -> number > 0;
        result = demo.numCheck(lb2, num);
        System.out.println("Using lambda expression: " + num + " is positive: " + result);
        
        // 使用IntPredicatesChecker的isPositive方法作为静态方法引用传递给numCheck()
        result = demo.numCheck(IntPredicatesChecker::isPositive, num);
        System.out.println("Using static method reference: " + num + " is positive: " + result);

    }
}

@FunctionalInterface
interface IntPredicate {
    boolean check(int i);
}

class IntPredicatesChecker {
    // 一个静态方法 用来检查number n 是否为正数
    public static boolean isPositive(int n) {
        return n > 0;
    }

    //一个静态方法 用来检查 number n是否为偶数
    public static boolean isEven(int n) {
        return (n % 2) == 0;
    }
}

输出如下

Using lambda expression: 11 is even: false
Using static method reference: 11 is even: false
Using lambda expression: 11 is positive: true
Using static method reference: 11 is positive: true

可以看到lambda表达式与静态方法引用的使用可以得到相同的结果。注意到 lambda表达式与静态方法的函数描述符相同,即参数为一个int类型,返回值为boolean类型。函数描述符相同则表明lambda与方法引用可以相互替换

对已存在对象的实例方法的引用

这里同样使用IntPredicate函数接口。同时声明了一个类IntNumChecker,该类有一个方法isBigger(),isBigger()方法与IntPredicate的check()具有相同的函数描述符。*IntPredicate p = checker::isBigger;*表明对象实例checker的isBigger方法传递给函数接口IntPredicate。

public class MethodRef {
    public static void main(String[] args) {
        IntNumChecker checker = new IntNumChecker(10);
        int numToCompare = 9;
        IntPredicate p = checker::isBigger;
        boolean result = p.check(numToCompare);
        if (result) {
            System.out.println(checker.getNum() + " is bigger than " + numToCompare);
        } else {
            System.out.println(checker.getNum() + " is smaller or equal than " + numToCompare);
        }
    }
}

@FunctionalInterface
interface IntPredicate {
    boolean check(int i);
}

class IntNumChecker {
    final private int num;

    public IntNumChecker(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    // 检查num是否大于输入的n
    boolean isBigger(int n) {
        return num > n;
    }
}

上述代码输出为

10 is bigger than 9
引用特定类型的任意对象的实例方法

这种类型是四种方法引用种最抽象的,很多文章都没有讲清楚。在这种实例中,可以使用类名替代对象名来引用方法。函数接口的第一个参数与调用的对象匹配,如本例的函数接口IntNumPredicate,其抽象方法的boolean check(IntNumChecker m, int n)的第一个参数即为IntNumChecker对象。这也是为什么该类型又被称为参数方法引用(parameter method reference)

public class MethodRef {
    public static void main(String[] args) {
        IntNumChecker checker1 = new IntNumChecker(10);
        int numToCompare = 9;
        IntNumPredicate p = IntNumChecker::isBigger;
        boolean result = p.check(checker1, 9);
        if (result) {
            System.out.println(checker1.getNum() + " is bigger than " + numToCompare);
        } else {
            System.out.println(checker1.getNum() + " is smaller  " + numToCompare);
        }

        // IntNumChecker第二个对象checker2
        IntNumChecker checker2 = new IntNumChecker(8);
        result = p.check(checker2, 9);

        if (result) {
            System.out.println(checker2.getNum() + " is bigger than " + numToCompare);
        } else {
            System.out.println(checker2.getNum() + " is smaller " + numToCompare);
        }
    }
}

@FunctionalInterface
interface IntNumPredicate {
    boolean check(IntNumChecker m, int n);
}

class IntNumChecker {
    final private int num;

    public IntNumChecker(int num) {
        this.num = num;
    }

    // 检查num是否大于n
    boolean isBigger(int n) {
        return num > n;
    }

    public int getNum() {
        return num;
    }
}

输出为

10 is bigger than 9
8 is smaller 9

IntNumPredicate p可以使用在任意checker1和checker2对象上。

引用构造器

构造器引用类似于静态函数引用。函数接口IntSupplier的抽象方法*ObjIntCreation apply(int n)*与ObjIntCreation的构造器的函数描述符相同,故可以使用ObjIntCreation::new调用。

public class MethodRef {
    public static void main(String[] args) {
        // 使用函数接口 IntSupplier
        int num = 10;
        IntSupplier s1 = ObjIntCreation::new;
        ObjIntCreation newObj1 = s1.apply(num);
        System.out.println("new object has a instance value " + newObj1.getNum());

        // 使用函数接口 IntObjectSupplier
        IntObjectSupplier s2 = ObjIntCreation::new;
        ObjIntCreation newObj = s2.apply(newObj1);
        System.out.println("new object has a instance value " + newObj.getNum());
    }
}

@FunctionalInterface
interface IntSupplier {
    ObjIntCreation apply(int n);
}

@FunctionalInterface
interface IntObjectSupplier {
    ObjIntCreation apply(ObjIntCreation obj);
}

class ObjIntCreation {
    private int num;

    public ObjIntCreation(int num) {
        this.num = num;
    }

    public ObjIntCreation(ObjIntCreation n) {
        this.num = n.num;
    }

    public int getNum() {
        return num;
    }
}

输出如下

new object has a instance value 10
new object has a instance value 10
方法引用优点

通过上述详细剖析方法引用的四种类型,既然方法引用可以与lambda表达式相互替换,那未为什么还要使用方法引用呢?以下将介绍方法引用相比于lambda表达式的优势在于:

1.代码可读性更好,因为具名

总结

本文详细介绍了方法引用的分类、使用方法以及优点。如发现任何问题,请留言交流。

参考

[1]Java Lambda: Method Reference
[2]Method References in Java

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值