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

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

相关推荐
小马爱打代码2 分钟前
Spring AI:ChatMemory 实现聊天记忆功能
java·人工智能·spring
小许学java7 分钟前
数据结构-模拟实现顺序表和链表
java·数据结构·链表·arraylist·linkedlist·顺序表模拟实现·链表的模拟实现
+VX:Fegn089526 分钟前
计算机毕业设计|基于springboot + vue零食商城管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
Query*42 分钟前
杭州2024.08 Java开发岗面试题分类整理【附面试技巧】
java·开发语言·面试
WZTTMoon1 小时前
Spring Boot 4.0 迁移核心注意点总结
java·spring boot·后端
旷野说1 小时前
为什么 MyBatis 原生二级缓存“难以修复”?
java·java-ee·mybatis
8***23551 小时前
【wiki知识库】07.用户管理后端SpringBoot部分
java
阿蔹1 小时前
JavaWeb-Selenium 配置以及Selenim classnotfound问题解决
java·软件测试·python·selenium·测试工具·自动化
小毅&Nora2 小时前
【后端】【C++】从裸指针到 C++20 协程:现代 C++ 内存与并发编程的双重革命
java·c++20
张np2 小时前
java基础-ArrayList
java·开发语言