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

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

相关推荐
CryptoRzz23 分钟前
欧美(美股、加拿大股票、墨西哥股票)股票数据接口文档
java·服务器·开发语言·数据库·区块链
杂货铺的小掌柜43 分钟前
apache poi excel 字体数量限制
java·excel·poi
大厂码农老A1 小时前
你打的日志,正在拖垮你的系统:从P4小白到P7专家都是怎么打日志的?
java·前端·后端
艾菜籽1 小时前
Spring MVC入门补充2
java·spring·mvc
爆更小哇1 小时前
统一功能处理
java·spring boot
程序员鱼皮1 小时前
我造了个程序员练兵场,专治技术焦虑症!
java·计算机·程序员·编程·自学
n8n2 小时前
SpringAI 完全指南:为Java应用注入生成式AI能力
java·后端
不爱编程的小九九2 小时前
小九源码-springboot082-java旅游攻略平台
java·开发语言·旅游
只是懒得想了2 小时前
用C++实现一个高效可扩展的行为树(Behavior Tree)框架
java·开发语言·c++·design-patterns
码农阿树2 小时前
Java 离线视频目标检测性能优化:从 Graphics2D 到 OpenCV 原生绘图的 20 倍性能提升实战
java·yolo·目标检测·音视频