【Java 干货教程】Java方法引用详解

导言

Java方法引用是Java 8引入的一项重要特性,它提供了一种简洁、可读性高的方式来直接引用已经存在的方法。方法引用使得代码更加简洁、易懂,同时提高了代码的可维护性和重用性。本文将详细介绍Java方法引用的概念、语法和使用方法,并提供一些示例代码。

一、方法引用的概念

方法引用是一种直接引用已经存在的方法的方式,它允许我们在代码中通过方法的名称来引用方法。方法引用可以被看作是Lambda表达式的一种简化形式,它提供了一种更加简洁的方式来实现函数式接口。

在Java中,方法引用主要用于简化函数式接口的实现,特别是当我们需要将一个方法作为参数传递给另一个方法时,使用方法引用可以使代码更加清晰。

二、方法引用的语法

方法引用的语法由两部分组成:类名或对象名和方法名,中间使用双冒号(::)进行分隔。根据方法引用的情况,可以分为以下几种形式:

  • 静态方法引用:类名::静态方法名
  • 实例方法引用:对象名::实例方法名
  • 特定类的任意对象方法引用:类名::实例方法名
  • 构造方法引用:类名::new

 2.1、静态方法引用

使用类名和双冒号(::)来引用一个静态方法。例如,Math::max表示引用Math类中的静态max方法。

Weigher integerIntegerWeigher = Math::max;

2.2、实例方法引用

使用实例对象和双冒号(::)来引用一个非静态的实例方法。例如,String::length表示引用String对象的length方法。

LineHandler lineHandler = String::length;

2.3、特定类的任意对象方法引用

使用特定对象的实例和双冒号(::)来引用该对象的实例方法。例如,myObject::methodName表示引用myObject对象的methodName方法。

Student student = new Student();
Copier getUsername = student::getUsername;

2.4、构造方法引用

Copier arrayListCopier = ArrayList::new;

三、方法引用的使用方法

方法引用主要用于函数式接口,即只包含一个抽象方法的接口。函数式接口可以使用方法引用来实现抽象方法,从而实现相应的功能。

Java中提供了许多函数式接口,如ConsumerPredicateSupplier等。可以使用方法引用来简化这些接口的实现,使代码更加简洁、易读。

下面是一些方法引用的使用示例:

3.1、使用方法引用实现Consumer接口

List numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(System.out::println);

forEach()方法源码

default void forEach(Consumer action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

这里的Consumer就是函数式接口

3.2、使用方法引用实现Supplier接口

Supplier> listSupplier = ArrayList::new;
List names = listSupplier.get();

Supplier也是函数式接口(源码)

@FunctionalInterface
public interface Supplier {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

四、案例 

以下是一个示例Java程序,演示了Java方法引用的四种使用形式:

import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

class Vehicle {
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    // 静态方法
    public static void printName(String name) {
        System.out.println(name);
    }

    // 实例方法
    public void printPowerValue(Long powerValue) {
        System.out.println(powerValue);
    }

    @Override
    public String toString() {
        return "Vehicle{" +
                "type='" + type + '\'' +
                '}';
    }
}

public class MethodReferenceExample {

    public static void main(String[] args) {
        // 静态方法引用: 类名::静态方法
        List names = Arrays.asList("擎天柱", "霸天虎", "威震天");
        names.forEach(Vehicle::printName);
        System.out.println();

        // 实例方法引用: 对象名::实例方法
        Vehicle example = new Vehicle();
        List messages = Arrays.asList(50000L, 120000L);
        messages.forEach(example::printPowerValue);
        System.out.println();

        // 构造方法引用: 类名::new
        Supplier vehicleSupplier = Vehicle::new;
        // 创建对象
        Vehicle car = vehicleSupplier.get();
        car.setType("Car");
        Vehicle bike = vehicleSupplier.get();
        bike.setType("Bike");
        // 打印对象
        System.out.println(car);
        System.out.println(bike);
        System.out.println();

        // 引用特定类型的任意对象的实例方法: 对象名::实例方法
        String prefix = "Prefix:";
        List suffixes = Arrays.asList("A", "B", "C");
        suffixes.stream()
                .map(prefix::concat) // 使用String的concat方法
                .forEach(System.out::println); // 使用System.out的println方法
    }
}

在这个示例中,我们使用了静态方法引用、实例方法引用、构造方法引用和引用特定类型的任意对象的实例方法。通过运行这个程序,可以看到各个方法引用形式的使用效果。

运行结果:

擎天柱
霸天虎
威震天

50000
120000

Vehicle{type='Car'}
Vehicle{type='Bike'}

Prefix:A
Prefix:B
Prefix:C

五、应用场景

方法引用广泛应用于函数式编程、Stream API和Lambda表达式等方面,特别在以下几个场景中被普遍使用:

1. 集合操作:在集合的排序、筛选、映射等操作时,可以使用方法引用简化代码;
2. GUI编程:在事件处理、回调函数等方面,通过方法引用来实现事件的响应机制;
3. 数据处理:在对数据进行加工、处理和计算时,方法引用常用于替代复杂的Lambda表达式。

想了解Stream API 和Lambda表达式和方法引用的场景可以看看我的这篇文章

【Java干货教程】Stream流详解-CSDN博客

六、注意事项

在使用方法引用时,有一些注意事项需要考虑

  1. 方法引用只能用于函数式接口:方法引用只能用于那些只有一个抽象方法的接口,也就是函数接口。确保在使用方法引用时,目标类型是一个函数接口,否则编译将会失败。
  2. 参数匹配:方法引用的参数必须与目标方法的参数列表兼容。这意味着参数数量和类型要一致。如果目标方法具有多个参数,可以通过上下文推断或者函数式接口的泛型来进行类型匹配。(方法引用的方法签名必须与函数式接口的抽象方法的参数列表和返回类型一致
  3. 方法引用可能引发空指针异常:在使用实例方法引用时,请确保对象引用不是null。否则,在调用实例方法时会引发空指针异常。因此,在进行实例方法引用操作前,请先对对象进行必要的空值检查。
  4. 正确使用方法引用,增加代码可读性:正确使用方法引用可以使代码更加简洁、清晰和易于理解。然而,过度使用方法引用可能会导致代码难以阅读和维护。请在合适的情况下使用方法引用,并在需要更明确的表达时使用传统的Lambda表达式(或匿名类)来代替。
  5. 选择最适合的方法引用方式:熟悉各种方法引用的不同形式,包括静态方法引用、实例方法引用、构造方法引用和引用特定类型的任意对象的实例方法。了解这些不同的使用形式,可以根据具体的编码需求选择最适合的方法引用方式。(方法引用不能直接引用抽象方法,也不能引用接口中的默认方法
  6. 方法引用与Lambda表达式的比较:在某些情况下,方法引用可以代替Lambda表达式来提高代码可读性。然而,并非所有情况都适合使用方法引用。有时,使用Lambda表达式可能更清晰明了。

七、优势和缺点

7.1、优点

  • 简化代码:方法引用可以将一些复杂的Lambda表达式转化为简洁明了的代码;
  • 提高代码的复用性:通过方法引用,我们可以重复使用已经存在的方法,而无需重复编写相同的代码。

7.2、缺点 

  • 可读性限制:某些情况下,过多的方法引用可能会降低代码的可读性,特别是当方法名并不直观或者存在多个参数的情况下;
  • 局限性:方法引用只能用于引用非静态方法、静态方法和构造方法(静态方法引用、实例方法引用、构造方法引用和引用特定类型的任意对象的实例方法),对于其他情况则无法使用(不能直接引用抽象方法,也不能引用接口中的默认方法)。

参考文章:【Java基础教程】(二十八)Java新特性篇 · 第八讲:方法引用——概念及优缺点、语法形式及使用案例、应用场景与注意事项 ~_java方法引用重要吗-CSDN博客

【Java 基础篇】Java方法引用详解-CSDN博客

你可能感兴趣的:(Java干货,java,方法引用,Java,8,新特性)