文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用Lombok的@Builder注解实现构造器模式

2024-12-02 02:54

关注

审校 | 梁策 孙淑娟

概述

我们将研究如何通过Lombok的@Builder注解让含有多个入参的方法更容易被调用,从而提高可用性。

使用@Builder的简单方法

怎样才能更灵活方便地调用多入参方法呢?请看下面的例子:

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

如果非空参数是可选的,那么有以下几种调用方式:

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

这个例子暴露了一些问题,比如:

从提供者的角度来看,如果要提供参数较少的方法则需要大量重载,如下:

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);
...

为了提高可用性且避免重复代码,我们可以使用方法构建器。Lombok项目已经提供了一个注解,它可以使生成构建器变得更简单。因此,上面的例子可以改造为以下方式:

@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();

这样一来,方法的可读性及灵活性都提高了。

默认值

在许多情况下,需要去为方法的入参指定默认值,而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时,会在目标类中生成一个构建器类。这个构建器类:

该类也可以手动定义,这样就可以为参数定义默认值。上面的方法也就可以写成:

@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类将是一个泛型类,但是builder方法将创建一个没有约束类型的实例。请看下面的例子:

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

在这种情况下,methodBuilder方法将创建一个没有约束类型的MethodBuilder实例。这导致下面的代码不会被编译(入参定义为Class,传入Class):

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

上述问题可以通过强制转换类型来解决:

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

这样,它就可以通过编译。但是这么使用,调用方法后返回的类型不是String,而是未知类型的泛型T。

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

那么,我们需要再将输出结果进行强制转换。而我们最初的目的是提供一种方便调用者的方法,因此建议考虑下面的两种解决方案之一。

覆盖Builder方法

如上所述,问题的根源在于builder方法在没有传入特定类型参数的情况下,创建了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方法中强制转换

也可以在类型参数set方法中强转构建器实例类型:

@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可以带来以下优势:

需要注意的是,在某些情况下,使用方法构建器会给提供者带来不必要的复杂情况。各类方法构建器的demo可参考GitHub上的信息。

译者介绍

翟珂,51CTO社区编辑,目前在杭州从事软件研发工作,做过电商、征信等方面的系统,享受分享知识的过程,充实自己的生活。

原文Method Builder With Lombok @Builder,作者:Daniel Buza

来源:51CTO内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯