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

总结

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

相关推荐
考虑考虑1 小时前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
martinzh2 小时前
Spring AI 项目介绍
后端
Bug退退退1233 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠3 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
前端付豪3 小时前
20、用 Python + API 打造终端天气预报工具(支持城市查询、天气图标、美化输出🧊
后端·python
爱学习的小学渣3 小时前
关系型数据库
后端
武子康3 小时前
大数据-33 HBase 整体架构 HMaster HRegion
大数据·后端·hbase