浅谈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可以帮我们自动变成方法引用。

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

相关推荐
步行cgn9 分钟前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
摇滚侠9 分钟前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
小蒜学长15 分钟前
基于Spring Boot+Vue3的在线医疗咨询系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·在线问诊·在线医疗咨询系统
Deng桑21 分钟前
SpringAI写第一个Agent-把工单循环跑起来
java·agent·工具调用·spring ai
步行cgn2 小时前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc
jason成都3 小时前
RTKLIB 的 Java 移植版来了:rtklib-java v3.3.0 功能全景
java·gnss·rtklib·rtklib_java
LazyHunter93 小时前
JDK 28:Java 下一步方向解析与新特性展望
java·jdk
m0_639906943 小时前
Java 重写 Override 与重载 Overload 区别、访问修饰符、static 关键字
java·开发语言
GIS数据转换器4 小时前
基于CIM的城市智慧供热监管服务平台
java·大数据·开发语言·人工智能·后端·物联网·struts
m0_639906944 小时前
SpringBoot自动配置原理
java·spring boot·后端