Skip to content

Java

方法引用

  • 方法: 以前的方法
  • 引用: 把以前的方法拿过来用, 当作函数式接口中抽象方法的方法体
java
//因为方法引用的格式阅读性不强, 所以会先使用匿名内部类和Lambda表达式,  比较着来看作用
import java.util.Arrays;
import java.util.Comparator;

public class Demo1 {
    public static void main(String[] args) {
        Integer[] arr = {21, 56, 12, 34};
        //匿名内部类
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        //lambda表达式
        Arrays.sort(arr, (o1, o2) -> o2 - o1);
        
        // Arrays.sort(arr, 比较规则)
        //在以前调用静态方法使用的是类型.方法名, 而方法引用形式也差不多
        // 1. 引用处必须是函数式接口
        Arrays.sort(arr, Demo1::subtraction);
    }
    // 2. 被引用的方法已经存在
    // 3. 引用的抽象方法参数类型和返回值类型, 与引用处参数类型和返回值类型一致
    // 4. 被引用方法的功能要满足当前需求
    public static int subtraction(int n1, int n2) {
        return n2 - n1;
    }
}

提示

::表示方法引用符

引用条件:

  1. 引用处必须是函数式接口
  2. 被引用的方法已经存在
  3. 引用的抽象方法参数类型和返回值类型, 与引用处参数类型和返回值类型一致
  4. 被引用方法的功能要满足当前需求

静态方法引用

  • 格式: 类名::方法名
  • 范式: Integer::parseInt

需求: 将集合中的元素转换为int类型

java
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;

public class Demo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "1", "2", "3", "4", "5");
        //对元素进行处理, 首先想到的是流; 然后我们需要将String转换为int类型, 那么想到的是map()
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        }).forEach(integer -> System.out.println(integer));
        
        //使用lambda表达式
        list.stream().map(s -> Integer.parseInt(s)).forEach(integer -> System.out.println(integer));

        //使用方法引用
        List<Integer> newList = list.stream()
        //需要实现的功能与parseInt一致, 并且返回值和参数类型也一致, 所以可以使用方法引用
        .map(Integer::parseInt)
        .collect(Collectors.toList());

        System.out.println(newList);// [1, 2, 3, 4, 5]
    }
}

成员方法引用

  • 格式: 对象::成员方法
    • 其他类: 其他类对象: 其他类对象::方法名
    • 本类: this::方法名
    • 父类: super::方法名

引用其他类的方法

java
public class Demo3 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤");
        //需求: 找到list集合中姓为张, 姓名长度为3的人
        list1.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 3 && s.startsWith("张");
            }
        }).forEach(s -> System.out.println(s));
        //方法引用 引用其他类的方法"对象::成员方法"
        list1.stream()
        .filter(new StringOperation()::stringJudge)
        .forEach(s -> System.out.println(s));
    }
}
java
public class StringOperation {
    //注: 引用的抽象方法参数类型和返回值类型, 与引用处参数类型和返回值类型一致
    public boolean stringJudge(String str) {
        return str.length() == 3 && str.startsWith("张");
    }
}

引用本类的成员方法

  1. 在静态方法中调用本类方法
java
public class Demo3 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤");
        //需求: 找到list集合中姓为张, 姓名长度为3的人
        list1.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 3 && s.startsWith("张");
            }
        }).forEach(s -> System.out.println(s));
        //因为static方法中, 不能使用this, 所以在静态方法中想要使用本类方法引用, 需要创建对象然后在引用
        list1.stream().filter(this::stringJudge).forEach(s -> System.out.println(s));
        //引用本类方法
        list1.stream().filter(new Demo3()::stringJudge).forEach(s -> System.out.println(s));
    }

    public boolean stringJudge(String str) {
        return str.length() == 3 && str.startsWith("张");
    }
}

  1. 在非静态方法中调用本类方法
java
@FunctionalInterface
public interface MyString {
    String mySubString(String s,int x,int y);
}

public class StringOperation {

    public StringOperation() {
        // useMyString((s,x,y) -> s.substring(x,y));
        useMyString(this::mySubstring);
    }

    private void useMyString(MyString my) {
        String s = my.mySubString("HelloWorld", 2, 5);
        System.out.println(s);
    }
    
    public String mySubstring(String s,int x,int y) {
        return s.substring(x, y);
    }
}

引用父类的成员方法

java
public class Fu {
    public String mySubstring(String s,int x,int y) {
        return s.substring(x, y);
    }
}

public class StringOperation extends Fu {

    public StringOperation() {
        useMyString(super::mySubstring);
    }

    private void useMyString(MyString my) {
        String s = my.mySubString("HelloWorld", 2, 5);
        System.out.println(s);
    }
    
}

构造方法引用

  • 格式: 类名::new
  • 目的: 创建这个类的对象
java
//Student构造方法, 若想要引用构造方法, 则对应的参数类型需要对应
public Student(String s) {
    String[] info = s.split("-");
    //为成员变量赋值, 就相当于new Student(name, age)
    this.name = info[0];
    this.age = Integer.parseInt(info[1]);
    //构造方法没有返回值, 但构造方法运行完后, 对象就已经有了, 所以返回值不需要管
    //只需要保证构造方法结束后, 生成的对象和抽象方法(比如当前的apply)返回值保持一致就行
}

public class Demo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("汪加年-19");
        list.add("小小汪-21");
        list.add("励志汪-20");

        //将list里面的数据转换为Student类型
        //以前的做法
        List<Student> studentList = list.stream().map(new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                String[] info = s.split("-");
                String name = info[0];
                int age = Integer.parseInt(info[1]);
                return new Student(name, age);
            }
        }).collect(Collectors.toList());
        //使用构造方法因哟女
        list.stream().map(Student::new).collect(Collectors.toList());
    }
}

类名引用成员方法

  • 格式: 类名::成员方法
  • 范式: String::substring

独属于类名引用成员方法的引用规则:

第三条规则与之前不同

  1. 引用处必须是函数式接口
  2. 被引用的方法已经存在
  3. 被引用的形参, 需要跟抽象方法的第二个形参到最后一个形参保持一致, 返回值需要保持一致
  4. 被引用方法的功能要满足当前需求
java
public class Demo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "aaa", "bbb", "ccc", "ddd", "eee");

        list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(System.out::println);
        System.out.println("----------------------");
        //类名引用成员方法
        //拿着流中的每一个数据, 去调用String类中的方法toUpperCase, 返回值就是转换之后的结果
        list.stream().map(String::toUpperCase).forEach(System.out::println);


    }
}

类名引用成员方法与接口中抽象方法的匹配规则

  1. 第一个参数 ==> 表示引用方法的调用者, 决定了可以引用哪些类中的方法. 例如: 流当中, 第一个参数一般表示流中的每一个数据, 假设流中的数据是字符串, 那么只能引用String类中的方法
  2. 第二个参数到最后一个参数, 抽象方法需要与引用方法保持一致, 如果抽象方法没有第二个参数, 说明被引用的方法需要的是无参的成员方法

局限性: 不能引用所有的成员方法


引用数组的构造方法

格式: 数据类型[]::new

java
public class Demo7 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5);

        //将集合转化为数组
        Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
            @Override
            public Integer[] apply(int value) {
                return new Integer[value];
            }
        });
        System.out.println(Arrays.toString(arr));
        //引用数组的构造方法就是创建一个数组
        Integer[] arr1 = list.stream().toArray(Integer[]::new);
        System.out.println(Arrays.toString(arr1));
    }
}

提示

list.stream().toArray(Integer[]::new);流中的数据是什么类型, 那么就创建什么类型的数组


总结

  1. 引用静态方法
    • 类名::静态方法名
  2. 引用成员方法
    1. 静态中引用
      • 对象::成员方法名
    2. 非静态中引用
      • this::成员方法名
      • super::成员方法名
  3. 引用构造方法
    • 类名::new
  4. 使用类名引用成员方法
    • 类名::成员方法名
  5. 引用数组的构造方法
    • 类名[]::new

Released under the MIT License.