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

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

相关推荐
九转苍翎1 分钟前
Java SE(6)——类和对象
java
孤海岛主13 分钟前
分布式链路ID实现
java·spring boot·分布式·spring cloud
冰^30 分钟前
深入Java JVM常见问题及解决方案
java·开发语言·jvm·spring boot·spring·mybatis·多分类
bing_1581 小时前
Spring MVC @PathVariable 注解怎么用?
java·spring·mvc
优雅的落幕1 小时前
Spring AOP---面向切面编程由认识到使用
java·后端·spring
子燕若水1 小时前
continue dev 的配置
java·服务器·前端
我是大头鸟1 小时前
SpringMVC 前后端数据交互 中文乱码
java·springmvc
嘵奇2 小时前
Spring Boot API版本控制实践指南
java·spring boot·后端
努力的搬砖人.2 小时前
redis常用集合操作命令
java·redis·后端
NullPointerExpection2 小时前
java 使用 POI 为 word 文档自动生成书签
java·word·poi