第1条:用静态工厂方法代替构造器

成神之路 - EffectiveJava给我的指引


对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,类可以提供一个公有的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。

  1. 传统方法
Person person = new Person();
Date date = new Date();
  1. 静态工厂方法
    除了公有的构造器之外,还可以通过提供静态工厂方法来实现获取类的实例。
// java源码
// 将boolean基本类型转换成一个Boolean对象引用
public static Boolean valueOf(boolean b) {
	return b ? Boolean.TRUE : Boolean.FALSE;
}

静态工厂方法与构造器不同的优势在于以下几点:

1.1 静态工厂方法有名称
java源码:构造器BigInteger(int, int, Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显然更为清楚。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器。如:

public class Person {
    private final String name;
    private final Hair hair;

    private Person(String name, Hair hair){
    	this.name = name;
        this.hair = hair;
    }
	// 黄头发人类
    public static Person getYellowHairInstance(String name){
        return new Person(name, Hair.yellow);
    }
    // 黑头发人类
    public static Person getBlackHairInstance(String name){
        return new Person(name, Hair.black);
    }
}

enum Hair {
    yellow,
    black;
}

1.2 不必在每次调用它们的时候都创建一个新对象。
这一小节主要讲了几个名词,总体来说就是能够为重复的调用返回相同的对象。
不可变类:当获得类的一个实例引用时,不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。
享元模式:主要用于减少创建对象的数量,以减少内存占用和提高性能。

1.3 它们可以返回原返回类型的任何子类型的对象
java源码中的Collections类中有一个静态方法unmodifiableSet,这个静态方法就是一个静态工厂方法,它可以返回原返回类型的任何子类型。

// Java源码
public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
	return new UnmodifiableSet<>(s);
}
static class UnmodifiableSet<E> extends UnmodifiableCollection<E> 
											implements Set<E>, Serializable {
    private static final long serialVersionUID = -9215047833775013803L;

    UnmodifiableSet(Set<? extends E> s)     {super(s);}
    public boolean equals(Object o) {return o == this || c.equals(o);}
    public int hashCode()           {return c.hashCode();}
}
// 示例
public static void main(String[] args) {
    Set<String> set = new LinkedHashSet<>();
    set.add("hello");
    set.add("csdn");
    set.add("friend");

    Set unSet = Collections.unmodifiableSet(set);
    unSet.add("error"); // UnsupportedOperationException

}

1.4 返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值
EnumSet 没有公有的构造器,只有静态工厂方法。在OpenJdk实现中,它们返回两种子类之一的一个实例,具体则取决于底层枚举类型的大小:如果它的元素有6 4个或者更少,就像大多数枚举类型一样,静态工厂方法就会返回一个RegularEnumSet实例,用单个long进行支持;

如果枚举类型有65个或者更多元素,工厂就返回JumboEnumSet实例,用一个long数组进行支持。

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
	Enum<?>[] universe = getUniverse(elementType);
	if (universe == null)
		throw new ClassCastException(elementType + " not an enum");

	if (universe.length <= 64)
		return new RegularEnumSet<>(elementType, universe);
	else
	return new JumboEnumSet<>(elementType, universe);
}

1.5 方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
服务提供者框架:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多个实现中解耦出来。
例如JDBC(Java数据库连接)API,Connection就是其服务接口的一部分,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver是服务提供者接口。

静态工厂方法的缺点如下:

2.1 类如果不含公有的或者受保护的构造器,就不能被子类化
例如,要想将Collections Framework中的任何便利的实现类子类化,这是不可能的。但是这样也许会因祸得福,因为它鼓励程序员使用符合而不是继承,这正是不可变类型所需要的。

2.2 程序员很难发现它们
对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类是非常困难的。但是我们可以在类或者接口注释中关注静态工厂,并遵守标准的命名习惯,可以弥补这一劣势。
下面是静态工厂方法的一些惯用名称。

// from - 类型转换方法,只有一个参数,返回该类型的一个相对应的实例
	Date d = Date.from(instant);
// of - 聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来
	Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
// valueOf - 比from和of更繁琐的一种替代方法
	BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
// instance或者getInstance - 返回的实例是通过方法的参数来描述的,但是不能说与参数具有同样的值
	StackWalker luke = StackWalker.getInstance(options);
// create或者newInstance - 每次调用都返回一个新的实例
	Object newArray = Array.newInstance(classObject, arrayLen);
// getType - 像getInstance 一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型
FileStore fs = Files.getFileStore(path);
// newType - 像newInstanfe 一样,但是在工厂方法处于不用的类中的时候使用,Type表示工厂方法返回的对象类型
BufferedReader br = Files.newBufferedReader(path);
// type - getType 和 newType 的简版,例如:
List<Complaint> litany = Collections.list(legacyLitancy);

本文完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

探_无止境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值