使用Lombok@Builder的方法Builder

946 阅读5分钟

在本教程中,我们将探讨使用Lombok的@Builder注释生成方法构建器的各种可能性,以提高可用性。

54cb0e1365ec6e0aa847c0acfb00deb1.jpeg

概述

在本教程中,我们将探讨用Lombok的方法生成方法构建器的可能性@Builder注释其目的是通过提供一种灵活的方法来调用给定的方法来提高可用性,即使它有很多参数。

@Builder关于简单方法

如何灵活地使用方法是一个普遍的话题,强权接受多个输入。请看以下示例:

void method(@NotNull String firstParam, @NotNull String secondParam, 
            String thirdParam, String fourthParam, 
            Long fifthParam, @NotNull Object sixthParam) {
    ...            
}

如果未标记为NOTNULL的参数是可选的,则该方法可能接受以下所有调用:

method("A", "B", null, null, null, new Object());
method("A", "B", "C", null, 2L, "D");
method("A", "B", null, null, 3L, this);
...

此示例已经显示了一些问题点,如:

  • 调用方应该知道哪个参数是哪个参数(例如,为了更改第一个调用以提供Long打电话的人也必须知道Long将是第五个参数)。
  • 输入必须按给定的顺序设置。
  • 输入参数的名称不透明。

同时,从提供程序的角度来看,提供更少参数的方法将意味着方法名称的大量重载,例如:

void method(@NotNull String firstParam, @NotNull String secondParam, @NotNull Object sixthParam);
void method(@NotNull String firstParam, @NotNull String secondParam, String thirdParam, @NotNull Object sixthParam);
void method(@NotNull String firstParam, @NotNull String secondParam, String thirdParam, String fourthParam, @NotNull Object sixthParam);
void method(@NotNull String firstParam, @NotNull String secondParam, String thirdParam, String fourthParam, Long fifthParam, @NotNull Object sixthParam);
...

为了获得更好的可用性和避免样板代码,可以引入方法构建器。ProjectLombok已经提供了一个注释,以使构建器的使用变得简单。上面的示例方法可以用以下方式进行注释:

@Builder(builderMethodName = "methodBuilder", buildMethodName = "call")
void method(@NotNull String firstParam, @NotNull String secondParam, 
            String thirdParam, String fourthParam, 
            Long fifthParam, @NotNull Object sixthParam) {
    ...            
}

因此,调用该方法如下所示:

methodBuilder()
        .firstParam("A")
        .secondParam("B")
        .sixthParam(new Object())
        .call();

methodBuilder()
        .firstParam("A")
        .secondParam("B")
        .thirdParam("C")
        .fifthParam(2L)
        .sixthParam("D")
        .call();

methodBuilder()
        .firstParam("A")
        .secondParam("B")
        .fifthParam(3L)
        .sixthParam(this)
        .call();

**这样,方法调用就更容易理解和更改。一些评论:

  • 默认情况下,静态方法上的构建器方法(获取构建器实例的方法)本身就是静态方法。
  • 默认情况下,call()方法将具有与原始方法相同的抛出签名。

默认值

在许多情况下,为输入参数定义默认值是非常有帮助的。与其他一些语言不同的是,Java没有支持这种需求的语言元素。因此,在大多数情况下,这是通过方法重载实现的,其结构如下:

method() { method("Hello"); }
method(String a) { method(a, "builder"); }
method(String a, String b) { method(a, b, "world!"); }
method(String a, String b, String c) { ... acutal logic here ... }

在使用Lombok构建器时,将在目标类中生成一个构建器类。这个建筑商类:

  • 具有与方法相同数量的属性和参数。
  • 它的论点是有策划者的。

还可以手动定义类,这样就可以为参数定义默认值。按照这种方式,上面的方法如下所示:

@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder")
method(String a, String b, String c) {
  ... acutal logic here ...
}

private class MethodBuilder {
    private String a = "Hello";
    private String b = "builder";
    private String c = "world!";
}

通过此添加,如果调用方不指定参数,则将使用生成器类中定义的默认值。

注意:在这种情况下,我们不必声明类中方法的所有输入参数。如果类中不存在该方法的输入参数,Lombok将相应地生成一个附加属性。

类型化方法

通常需要通过输入之一定义给定方法的返回类型,例如:

public <T> T read(byte[] content, Class<T> type) {...}

在这种情况下,构建器类也将是一个类型化的类,但是构建器方法将创建一个没有有界类型的实例。请看以下示例:

@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder")
public <T> T read(byte[] content, Class<T> type) {...}

在这种情况下,methodBuilder方法将创建MethodBuilder没有有界类型参数。这导致以下代码不会编译(这是Class<T>,并由Class<String>):

methodBuilder()
    .content(new byte[]{})
    .type(String.class)
    .call();

的输入可以解决这一问题。type并将其用作:

methodBuilder()
    .content(new byte[]{})
    .type((Class)String.class)
    .call();

它将编译,但还需要提到另一个方面:在本例中,返回类型Call方法不会是字符串,但仍然是未绑定的T。因此,客户端必须按如下方式转换返回类型:

String result = (String)methodBuilder()
    .content(new byte[]{})
    .type((Class)String.class)
    .call();

这个解决方案可以工作,但它也需要调用者同时转换输入和结果。由于最初的动机是提供一种调用友好的方法来调用这些方法,因此建议考虑以下两个选项之一。

重写Builder方法

如上所述,问题的根源在于构建器方法在没有特定类型参数的情况下创建构建器类的实例。仍然可以在类中定义构建器方法,并使用所需的类型创建构建器类的实例:

@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder")
public <T extends Collection> T read(final byte[] content, final Class<T> type) {...}

public <T extends Collection> MethodBuilder<T> methodBuilder(final Class<T> type) {
    return new MethodBuilder<T>().type(type);
}

public class MethodBuilder<T extends Collection> {
    private Class<T> type;
    public MethodBuilder<T> type(Class<T> type) { this.type = type; return this; }
    public T call() { return read(content, type); }
}

在这种情况下,调用者不必在任何时候进行强制转换,调用如下所示:

List result = methodBuilder(List.class)
    .content(new byte[]{})
    .call();

在设置器中进行铸造

还可以在类型参数的setter中强制转换构建器实例:

@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder")
public <T extends Collection> T read(final byte[] content, final Class<T> type) {...}

public class MethodBuilder<T extends Collection> {
    private Class<T> type;
    public <L extends Collection> MethodBuilder<L> type(final Class<L> type) { 
        this.type = (Class)type; 
        return (MethodBuilder<L>) this;
    }
    public T call() { return read(content, type); }
}

结语

在方法上使用@Builder可以带来以下优点:

  • 在呼叫方方面有更大的灵活性
  • 没有方法重载的默认输入值
  • 改进方法调用的可读性
  • 允许通过同一个构建器实例进行类似的调用。

4759571d59886f6304b656e1f220c325.jpeg 小伙伴们如果觉得我写的不错,不妨帮个忙,给我点个赞呗,可以让更多的人看到这篇文章. 完整资料已经给大家打包完毕,需要的小伙伴可以点击获取学习资料