JDK8新特性---方法引用

259 阅读4分钟

JDK8新特性---方法引用

什么是方法引用呢?

其实这个东西,在我之前的文章已经有写到,但是,也只是写到,我没有详细的说明,现在我们来看一个例子

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class FunctionRefTest1 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三",20));
        list.add(new Student("李四",19));
        list.add(new Student("王五",23));
        list.add(new Student("刘六",22));
        //通过Comparator对list排序
        // lambda的简略写法
        // list.sort((o1,o2)-> o1.getAge()-o2.getAge());
        // lambda最简单的写法
        list.sort( Comparator.comparingInt(Student::getAge));
        for (Student student : list) {
            System.out.println(student.getName()+" "+student.getAge());
        }
    }

}

在代码中,我们可以发现,list.sort((o1,o2)-> o1.getAge()-o2.getAge());list.sort( Comparator.comparingInt(Student::getAge));这两段代码的效果是一样的,而list.sort( Comparator.comparingInt(Student::getAge));这个就是使用了方法引用,效果是什么呢?方法引用使得lambda表达式不显得那么冗余,也就是简化了lambda表达式的写法格式。

初步体验

  • 方法引用可以理解为消除lambda表达式的冗余问题,简化书写(还有点小装逼的意味在里面,你看得懂,别人看不懂,就可以装逼)
  • 格式: AA::BB 具体AA是什么,BB是什么,就要看具体情况了,下面我会详细说明

可能你现在还是蒙蒙的,没事,再来一个例子,让你消化一下

public class FunctionRefTest2 {
    public static void main(String[] args) {
        getSum((a)->{
            int ans = 0;
            for (int i : a) {
                ans += i;
            }
            System.out.println("sum = "+ans);
        });
    }
    public static void getSum(Consumer<int[]> consumer){
        int[] a = {1,2,3,4,5,6,7,8,9};
        consumer.accept(a);
    }
}

在这里我们要实现数组求和,我们这样写没问题,但是我们会发现lambda{}内的内容很多,而且,如果在外面有个方法也可以实现数组求和,就像这样

public class FunctionRefTest2 {
    public static void main(String[] args) {
        getSum((a)->{
            int ans = 0;
            for (int i : a) {
                ans += i;
            }
            System.out.println("sum = "+ans);
        });
    }
    public static void sum(int[] a){
        int ans = 0;
        for (int i : a) {
            ans += i;
        }
        System.out.println("sum = "+ans);
    }
    public static void getSum(Consumer<int[]> consumer){
        int[] a = {1,2,3,4,5,6,7,8,9};
        consumer.accept(a);
    }
}

外面的sum方法和lambda表达式的{}内的代码就重复了,这样就出现了lambda表达式冗余的情况了,这时候,方法引用就发挥了它的作用

public class FunctionRefTest2 {
    public static void main(String[] args) {
        getSum(FunctionRefTest2::sum);
    }
    public static void sum(int[] a){
        int ans = 0;
        for (int i : a) {
            ans += i;
        }
        System.out.println("sum = "+ans);
    }
    public static void getSum(Consumer<int[]> consumer){
        int[] a = {1,2,3,4,5,6,7,8,9};
        consumer.accept(a);
    }
}

怎么样,lambda表达式是不是更简洁了,逼格更高了?相信到这里,你已经对方法引用已经有一些了解了,接下来,让我们再去看看它的其他内容

方法引用的方式

  1. 对象名::方法名
  2. 类名::静态方法名
  3. 类名::引用实例方法
  4. 类名::构造器
  5. 数组::构造器

方法引用的注意事项:

  • 被引用的方法,参数要和接口中的抽象方法的参数—样
  • 当接口抽象方法有返回值时,被引用的方法也必须有返回值
  • 方法引用只能引用已经存在的方法

没有实例怎么行呢,来几个实例看看,深入了解一下

对象名::方法名

这是最常见的一种用法。如果一个类中的已经存在了一个成员方法,则可以通过对象名引用成员方法

package com.features.functionReference;

import java.util.Date;
import java.util.function.Supplier;

public class FunctionRefTest3 {
    public static void main(String[] args) {
        Date now = new Date();
        Supplier<Long> s1 = () -> now.getTime();
        System.out.println("未使用方法引用得到的当前时间:"+s1.get());
        Supplier<Long> s2 = now::getTime;
        System.out.println("使用方法引用得到的当前时间:"+s2.get());
    }
}

image.png

now是Date类型的一个对象

image.png

类名::静态方法名

常见的形式之一

package com.features.functionReference;

import java.util.function.Supplier;

// 类名::静态方法名
public class FunctionRefTest4 {
    public static void main(String[] args) {
        Supplier<Long> s1 = ()-> System.currentTimeMillis();
        System.out.println("未使用方法引用得到的当前时间:"+s1.get());
        Supplier<Long> s2 = System::currentTimeMillis;
        System.out.println("未使用方法引用得到的当前时间:"+s2.get());
    }
}

image.png

System是一个类

image.png

类名::引用实例方法

Java面向对象中,类名只能调用静态方法类名引用实例方法是用前提的,实际上是拿第一个参数作为方法的调用者

package com.features.functionReference;

import java.util.function.BiFunction;
import java.util.function.Function;

// 类名::引用实例方法
public class FunctionRefTest5 {
    public static void main(String[] args) {
        Function<String,Integer> f1 = s -> s.length();
        System.out.println("未使用方法引用得到的hello的长度为:"+f1.apply("hello"));
        Function<String,Integer> f2 = String::length;
        System.out.println("使用方法引用得到的hello的长度为:"+f2.apply("hello"));

        BiFunction<String,Integer,String> f3 = String::substring;
        System.out.println("使用方法引用得到的hello的长度为:"+f3.apply("hello",2));
    }
}

image.png

s是String的一个实例,而length是s的实例方法

image.png

类名::构造器

由于构造器的名称和类名完全一致,所以构造器引用使用:: new的格式使用,

package com.features.functionReference;

import com.features.lambda.demo02.Student;

import java.util.function.BiFunction;
import java.util.function.Supplier;

// 类名::构造器
public class FunctionRefTest6 {
    public static void main(String[] args) {
        Supplier<Student> s1 = () -> new Student();
        System.out.println("未使用方法引用前得到的结果:"+s1.get());

        Supplier<Student> s2 = Student::new;
        System.out.println("使用方法引用后得到的结果:"+s2.get());

        BiFunction<String,Integer,Student> function = Student::new;
        System.out.println("使用方法引用后得到的结果:"+function.apply("张三",20));
    }
}

image.png

Student是一个具体的类

image.png

数组::构造器

package com.features.functionReference;

import java.util.function.Function;

// 数组::构造器
public class FunctionRefTest7 {
    public static void main(String[] args) {
        Function<Integer,String[]> f1 = len->new String[len];
        System.out.println("未使用方法引用前得到的数组的长度:"+f1.apply(5).length);

        Function<Integer,String[]> f2 = String[]::new;
        System.out.println("使用方法引用后得到的数组的长度:"+f2.apply(5).length);
    }
}

image.png

image.png