Effective Java 用静态工厂方法代替构造器

序言

最近需求写完, 测试也测完发布到rc环境后, 有一个涉及第三方接口的地方需要做一点调整, 于是就在等待第三方回应的闲暇之余拿起来几年前买的一本好书 << Effective Java>> 第三版来重读下部分内容, 今天分享的就是第二章的第一条: 用静态工厂方法代替构造器.

首先我打开自己写的代码, 尤其是一些业务对象, 大多数的类都是这么定义的:

java 复制代码
@Data
public class TestModel {
    private Long xx;
    private String yy;

使用的时候就是 new TestModel(xx,yy) 这样, 这是非常合乎常理的一种使用方式. 但是这里本书就提到了, 我们程序员的工具箱里应该还有一个创建对象实例的方法, 就是让类提供一个或多个公有的静态工厂方法(static factory method), 而且在Java源码里面就有很多地方用到并且我们会经常用到, 比如: BigDecimal.valueOf, Date.from, LocalDate.of...

静态工厂方法的优势

在有了构造方法后为什么还要这么建议呢? 在书里给出了5条理由:

  1. 有名字
  2. 有需求的时候, 不必每次都创建一个新对象
  3. 返回对象类型可以是返回类型的任何子类型的对象
  4. 返回对象的类型可以根据参数值的变化而变化
  5. 返回对象的具体类型可以不存在

让我来逐个演示下这五个优势的具体体现:

  1. 有名字
java 复制代码
public static TestModel withIdAndName(Long id, String name) {
    TestModel testModel = new TestModel();
    testModel.setId(id);
    testModel.setName(name);
    return testModel;
}

这种随便自定义名称的方法肯定比冰冷的构造器更容易理解.

  1. 有需求的时候, 不必每次都创建一个新对象
java 复制代码
public static TestModel commonInstance() {
    return EmptyModel.INSTANCE;
}

该方法每次放回的都是同一个对象, 我这里实现的方法是通过静态内部类的方式类实现的, 也可以通过其他单例模式的实现来实现该需求. 这里需要注意的是, 由于是同一个对象, 需要考虑引用传递的问题.

  1. 返回对象类型可以是返回类型的任何子类型的对象
csharp 复制代码
public static TestModel modelA() {
    return new ModelA();
}

很明显这里返回的是TestModel的子类 ModelA 的对象实例.

  1. 返回对象的类型可以根据参数值的变化而变化
typescript 复制代码
public <T extends TestModel> TestModel typeModel(T type) {
    if(type instanceof ModelA a) {
        return a;
    }else if(type instanceof ModelB b) {
        return b;
    }
    return null;
}

这个方法返回对象的类型就取决于参数type的具体类型.

  1. 返回对象的具体类型可以不存在
csharp 复制代码
public static PostgresqlConnect connect() {
    return ServiceLoader.load(PostgresqlConnect.class).findFirst().orElse(null);
}

不知道这里我理解的对不对, 这里方法的返回值是一个接口, 具体的实现类在方法调用的时候去指定的位置加载, Java的SPI机制应该也是基于这个基础.

静态工厂方法的劣势

说完这么多优点, 还有两个缺点也要交待一下:

  1. 类如果不含有公有的或者受保护的构造器, 就不能被子类化
  2. 程序员很难去发现他们. 通常在API文档里不会像描述构造器一样去描述静态构造方法, 但是可以通过标准的命名习惯可以些微弥补这个缺点, 列举一些在JDK源码中常用的名称: from, of, valueOf, instance/getInstance, create/newInstance, getType, newType, type...

总结

静态工厂方法和公有构造器各有特点, 在写代码的时候切记第一反应是构造器而完全忽略静态工厂.

相关推荐
ServBay5 分钟前
DeepSeek 再次崩了,DeepSeek V5正在路上?
后端·deepseek
后台模板学习23 分钟前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
w_zero_one23 分钟前
算法--构造
java·数据结构·算法
SL_staff37 分钟前
制造业订单交付崩盘?用四层协同框架重建交付可信度(技术实践)
java·产品·全栈
IT规划师1 小时前
编程术语中的同步与异步:从"等不等"到工程取舍
后端·面试
SL_staff1 小时前
打破风控迭代死循环:用决策编排重构业务与技术协作链路
java·产品·全栈
孟陬2 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
GoGeekBaird2 小时前
别再给每个 AI 做自我介绍了:我做了一个「个人上下文」管理系统
后端·github·agent
码事漫谈2 小时前
腾讯混元 Hy4 正式版发布了
后端
Mikko72 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端