JDK新特性(三)——方法引用

前言

JDK8在推出Lambda表达式的同时,还推出了方法引用这个概念,利用方法引用我们可以让我们的代码更加简洁,同时减少重复性代码的编写。本篇文章将对方法引用的使用和注意事项进行讲解,希望各位读者能够有所收获。

本篇文章的内容会涉及到Lambda表达式,如果你对Lambda表达式不了解的话,建议先看本系列文章:
JDK新特性(一)——Lambda表达式


一、方法引用介绍

(一)方法引用出现的原因

在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?
答案肯定是没有必要
我们以下面的代码作为一个例子

public interface User {
    void save(String printStr);
}

public class MethodReferenceTest {
    public static void main(String[] args) {
        useSave(s-> System.out.println(s));
    }

    static void useSave(User user){
        user.save("调用了User的save方法");
    }
}

我们可以看到实际上,我们只是想要在接口方法中实现打印传入的字符串这个功能,我们在实现方法中调用了System.out对象的print方法,也就是说这个方法本质上和直接调用System.out.println输出文字没有什么不同,那我们能不能把待实现的方法去引用已经存在的方案呢?
这自然是可以的,我们来看一下方法引用可以怎么样来解决这个问题

public class MethodReferenceTest {
    public static void main(String[] args) {
        useSave(System.out::println);
    }

    static void useSave(User user){
        user.save("调用了User的save方法");
    }
}

我们可以看到,首先使用了方法引用后,代码变得更加简洁了(当然了,从另外一个角度来说也易读性也变差了(─.─||)),第二点是user.save方法本质上调用的就是System.out.print方法。

(二)方法引用的使用语法
方法引用符

:: 该符号为引用运算符,而它所在的表达式被称为方法引用

推导与省略

如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导如果使用方法引用,也是同样可以根据上下文进行推导。简单理解就是,方法引用和Lambda表达式一样,编译器都可以通过原接口的参数类型来推导出方法应该怎么执行和参数应该怎么传递。

二、方法引用的使用

方法引用根据引用的对象可以分为四种类型,分别是引用类(静态)方法、引用类的实例方法、引用类的构造方法和引用对象的实例方法,下面就来看一下分别是怎么使用的吧。

引用类(静态)方法

这个很好理解,就是我们可以直接引用类的静态方法

public interface Converter {

    int convert(String num);
}

public class MethodReferenceTest {
    public static void main(String[] args) {
        // 使用Lambda表达式
        // 其实这个方法本质上就是调用了Integer的parseInt方法,我们没必要再写一次
        useConverter(num -> Integer.parseInt(num));
        
        // 使用方法引用
        useConverter(Integer::parseInt);
    }
    static void useConverter(Converter converter){
        int num = converter.convert("123");
        System.out.println(num);
    }
}

Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

引用类实例方法
public interface MyString {
    
    String subString(String str,int startIdx,int endInx);   
}

public class MethodReferenceTest {
    public static void main(String[] args) {
        // 使用Lambda表达式
        // 其实这个方法本质上就是调用了String的 substring 方法,我们没必要再写一次
        useMyString((str,a,b)-> str.substring(a,b));

        // 使用方法引用
        useMyString(String::substring);
    }

    static void useMyString (MyString myString){
        String result = myString.subString("hello method reference",0 ,6);
        System.out.println(result);
    }
}

Lambda表达式被类的实例方法替代的时候第一个参数作为调用者后面的参数全部传递给该方法作为参数
原因也很好理解,一般来说类中的方法都是需要其实例对象才可以调用的,我们要想调用类的实例方法,自然需要给它传递实例对象。

引用构造器
格式   类名::new
范例  Student::new
public class Student {

    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() { return age;}
    public void setAge(int age) {this.age = age;}

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public interface StudentFactory {

    Student getStudent(String name,int age);
}

public class MethodReferenceTest {

    public static void main(String[] args) {
        // 使用Lambda表达式
        // 其实这个方法本质上就是调用了 Student 类的带参构造方法,我们没必要再写一次
        useStudentFactory((name,age) -> new Student(name,age));

        // 使用方法引用
        useStudentFactory(Student::new);
    }

    static void useStudentFactory (StudentFactory sf){
        Student student = sf.getStudent("小明", 14);
        System.out.println(student);
    }

}

Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数

引用对象的实例方法
格式   对象::成员方法
范例   "HelloWorld"::toUpperCase
public class PrintUtils {

    public void upperAndPrintWords(String str){
        String result = str.toUpperCase();
        System.out.println(result);
    }
}

public interface MyString {

    void printUpperWords(String str);
}

public class MethodReferenceTest {

    public static void main(String[] args) {
        // 使用Lambda表达式
        // 其实这个方法本质上就是调用了 PrintUtils 类的 upperAndPrintWords 方法,我们没必要再写一次
        useMyString(s ->{
            String result = s.toUpperCase();
            System.out.println(result);
        });
        
        // 使用方法引用
        useMyString(new PrintUtils()::upperAndPrintWords);
    }

    static void useMyString(MyString myString){
        myString.printUpperWords("hello method reference");
    }
}

Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数

说在最后

其实在笔者看来,方法引用的概念并不难理解,本质上这个特性就是给已存在的方法提供引用,在某些场景下:比如需要在新的方法中调用已存在的方法 ,我们可以直接省略了上面这个步骤,而是直接引用已存在的方法,编译器就自然知道应该去调用哪个方法。

posted @   moutory  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示