浅谈Java方法引用

前言

最近在学黑马点评项目,发现里面用到了stream、方法引用等知识,因此对这些基础知识又进行了学习。

什么是方法引用

Java的方法引用可以理解为Lambda表达式的一种特殊形式,简化代码书写。

具体分类

  • 静态方法引用
  • 实例方法引用
  • 构造方法引用
  • 引用数组的构造方法

对于构造方法引用和数组的构造方法这里不多赘述,本文主要针对本人在学习当中对于实例方法是否需要new对象进行探讨,且主要针对如果使用而非具体原理。

静态方法引用

最典型的莫过于Integer了

Java 复制代码
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3");
list.stream().map(Integer::parseInt);

构造方法引用

类名::new

eg:Student::new

引用数组的构造方法

Java 复制代码
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3);
Integer[] arr = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr));

接下来重点讨论实例方法引用

实例方法引用

Java自带的类

对于Java自带的类来说,基本可以直接用类名,不需要new

Java 复制代码
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "bbb", "ccc");
list.stream().map(String::toUpperCase);

自定义类

对于自定义类来说,需要new

先定义一个学生类

Java 复制代码
public class Student {
    private String name;
    private int age;

    public boolean adult(Integer age){
        return age >= 18;
    }
}

再进行引用

Java 复制代码
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 16, 20, 18);
list.stream().filter(new Student()::adult);

下面再引用黑马点评项目中的一段代码

ini 复制代码
// 1.查询店铺信息
List<Shop> list = shopService.list();
// 2.根据店铺类型分组
Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));

当时我就很纳闷为什么这个实例方法不需要用new呢?

后面发现这里List<Shop> list = shopService.list(); 中的尖括号中的 Shop 表示这个 List 集合中元素的类型是 Shop 类型。也就是说,list 是一个包含 Shop 对象的集合。

当我们使用 list.stream().collect(Collectors.groupingBy(Shop::getTypeId)) 这行代码时,我们已经有了一个由 Shop 对象组成的流,因为 list 集合中存储的就是 Shop 对象。

举个例子

还是刚刚那个Student类

Java 复制代码
ArrayList<Student> list2 = new ArrayList<>();
Collections.addAll(list2,
        new Student("张三",16),
        new Student("李四",20),
        new Student("王五",18)
);
list2.stream().filter(Student::adult).forEach(System.out::println);

可以理解为list2中已经有Student对象了,但是,如果还是刚刚那个类,将会报错!

得加一个空参方法

Java 复制代码
public boolean adult(){
    return age >= 18;
}

为什么呢?我们来对比一下

Java 复制代码
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 16, 20, 18);
list.stream().filter(new Student()::adult).forEach(System.out::println);
list.stream().filter(s->new Student().adult(s)).forEach(System.out::println);
list.stream().filter(new Predicate<Integer>() {
    @Override
    public boolean test(Integer integer) {
        return false;
    }
});
//-------------------------------------------------------------------
ArrayList<Student> list2 = new ArrayList<>();
Collections.addAll(list2,
        new Student("张三",16),
        new Student("李四",20),
        new Student("王五",18)
);
list2.stream().filter(Student::adult).forEach(System.out::println);
list2.stream().filter(s->s.adult()).forEach(System.out::println);
list2.stream().filter(new Predicate<Student>() {
    @Override
    public boolean test(Student student) {
        return false;
    }
});
  1. 第一种获得的是Integer,需要有参的方法(有参数时)

在第一种情况中,使用了 list 这个 ArrayList 来存储整数类型的数据,然后通过 stream() 方法来创建一个流,并使用 filter 方法进行过滤。在 filter 方法中,使用了 new Student()::adult 这样的方法引用,假设 Student 类中有一个实例方法 adult,那么它的方法签名应该是 public boolean adult(Integer age),这是因为方法引用 new Student()::adult 等价于使用 Lambda 表达式 (s) -> new Student().adult(s),因此需要一个参数 age

  1. 而第二种得到的是Student,需要无参方法(已经存在对象时)

而在第二种情况中,使用了 list2 这个 ArrayList 来存储 Student 对象,然后也是通过 stream() 方法创建流并使用 filter 方法进行过滤。在 filter 方法中,使用了 Student::adult 这样的方法引用,假设 Student 类中有一个实例方法 adult,那么它的方法签名应该是 public boolean adult(),因为在这种情况下,Student::adult 等价于使用 Lambda 表达式 (s) -> s.adult(),不需要传入参数,因为 s 已经作为隐含参数传入了 adult 方法。

小结一下,所以对于自定义类的实例方法来说,

  1. 如果使用时用的是参数,则需要new,且要有对应的有参方法
  2. 如果使用时用的是已有对象,则不需要new,但是要有对应的无参方法

总结

当然,如果读者觉得这个方法引用过于复杂,可以写Lambda表达式,我们的Idea可以帮我们自动变成方法引用。

这些只是本人的拙见,可能存在错误,望大佬们指出。

相关推荐
Funcy12 分钟前
XxlJob 源码分析04:admin与executor通讯
java
托比-马奎尔15 分钟前
初识SpringBoot
java·spring boot·后端
前行的小黑炭23 分钟前
Android :如何提升代码的扩展性,方便复制到其他项目不会粘合太多逻辑,增强你的实战经验。
android·java·kotlin
-凌凌漆-24 分钟前
【Qt】【C++】虚析构函数及 virtual ~Base() = default
java·c++·qt
凯尔萨厮36 分钟前
Java学习笔记四(继承)
java·笔记·学习
Mr_Xuhhh41 分钟前
项目-sqlite类的实现
java·jvm·sqlite
孫治AllenSun1 小时前
【Springboot】介绍启动类和启动过程
java·spring boot·后端
励志码农5 小时前
JavaWeb 30 天入门:第二十三天 —— 监听器(Listener)
java·开发语言·spring boot·学习·servlet
@小匠5 小时前
Spring Cache 多租户缓存隔离解决方案实践
java·spring·缓存