Lombok 中的 @ExtensionMethod 注解

1、概览

Lombok 是一个流行的 Java 库,它通过减少模板代码来简化代码编写。其强大功能之一是 @ExtensionMethod 注解,可以增强代码的可读性和简洁性。

本文将带你了解 Lombok 中的 @ExtensionMethod 注解有什么用,以及如何有效地使用它。

2、@ExtensionMethod 是什么?

@ExtensionMethod 注解允许为现有类添加静态方法扩展。这意味着可以在原始类中调用在其他类中定义的方法。这有利于在不修改源代码的情况下增强第三方库或现有类的功能。

3、@ExtensionMethod 的原理

要使用 @ExtensionMethod,需要用 @ExtensionMethod 来注解一个类,并指定包含想要扩展的静态方法的类。然后,Lombok 会生成必要的代码,使这些方法可用,就好像它们是注解类的方法一样。

假设我们有一个工具类 StringUtils,其中的 reverse() 方法可以反转字符串。我们想把这个方法当作 String 类的一个方法来使用。Lombok 的 @ExtensionMethod 可以帮助我们实现这一目的。

首先,需要在项目中添加 Lombok 依赖。如果使用 Maven,可以在 pom.xml 中添加以下内容:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

Lombok 的最新版本可以在 Maven 中央仓库 找到。

3.1、String 示例

先创建 StringUtils 工具类,实现 reverse 方法:

public static String reverse(String str) {
    return new StringBuilder(str).reverse().toString();
}

接下来,创建一个使用 @ExtensionMethod 注解的测试类:

@ExtensionMethod(StringUtils.class)
public class StringUtilsUnitTest {
    @Test
    public void givenString_whenUsingExtensionMethod_thenReverseString() {
        String original = "Lombok Extension Method";
        String reversed = original.reverse();
        assertEquals("dohteM noisnetxE kobmoL", reversed);
    }
}

在上述代码中,StringUtils 类包含一个静态方法 reverse(),该方法接收一个字符串并返回其反转后的结果。StringUtilsUnitTest 类注解了 @ExtensionMethod 这告诉 Lombok 将 StringUtils 的静态方法视为其他类的扩展方法。在测试方法中,调用了 original.reverse()

虽然 String 并没有 reverse() 方法,但 Lombok 允许这种调用,因为 StringUtils 有一个静态方法 reverse(),它的第一个参数是一个 String

如果你检查一下 Lombok 生成的类,就会发现 Lombok 会在编译过程中将 original.reverse() 调用重写为 StringUtils.reverse(original)。这种转换说明,original.reverse() 是 Lombok 为提高代码可读性而提供的语法糖:

下面是 Lombok 生成的类的样子,包括一个生成的静态方法:

private static String reverse(String str) {
    return StringUtils.reverse(str);
}

再看看测试用例,看看 Lombok 对哪个部分进行了转换:

@Test
public void givenString_whenUsingExtensionMethod_thenReverseString() {
    String original = "Lombok Extension Method";
    String reversed = reverse(original); 
    assertEquals("dohteM noisnetxE kobmoL", reversed);
}

Lombok 会对 reversed 变量赋值的代码部分进行转换。

如果上例中没有使用 @ExtensionMethod 注解。在这种情况下,需要调用工具类中的方法,这会使代码变得更加冗长,也不那么直观。

下面是没有 @ExtensionMethod 注解时的代码:

public class StringUtilsWithoutAnnotationUnitTest {
    @Test
    public void givenString_whenNotUsingExtensionMethod_thenReverseString() {
        String original = "Lombok Extension Method";
        // 调用工具类方法
        String reversed = StringUtils.reverse(original);
        assertEquals("dohteM noisnetxE kobmoL", reversed);
    }
}

如上,使用 StringUtils 调用了 reverse() 方法。

3.2、List 示例

使用 List 创建一个示例,并演示如何使用 @ExtensionMethod 注解添加一个可对 List 对象进行操作的工具方法。

创建一个带有 sum() 方法的工具类 ListUtils,该方法可计算 List<? extends Number> 中所有元素的总和。然后,使用 @ExtensionMethod 注解将此方法用作 List 类的一部分:

public static int sum(List<? extends Number> list) {
    return list.stream().mapToInt(Number::intValue).sum();
}

测试 IntegerDouble 集合类型的测试类如下:

@ExtensionMethod(ListUtils.class)
public class ListUtilsUnitTest {
    @Test
    public void givenIntegerList_whenUsingExtensionMethod_thenSum() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int total = numbers.sum();
        assertEquals(15, total, "The sum of the list should be 15");
    }

    @Test
    public void givenDoubleList_whenUsingExtensionMethod_thenSum() {
        List<Double> numbers = Arrays.asList(1.0, 2.0, 3.0);
        int total = numbers.sum();
        assertEquals(6, total, "The sum of the list should be 6");
    }
}

测试类使用 @ExtensionMethod(ListUtils.class) 注解将 sum() 视为 List 类的扩展方法。

这样就可以直接调用 numbers.sum()

这也凸显了 Lombok 如何充分应用泛型来找出扩展方法。这使得 @ExtensionMethod 注解可以与特定类型的集合配合使用。

4、总结

本文介绍了如何通过 Lombok 中的 @ExtensionMethod 注解,在不修改源代码的情况下增强现有类的功能。这使我们的代码更具表现力,也更易于维护。


Ref:https://www.baeldung.com/java-lombok-extensionmethod