Effective Java:Joshua Bloch著,第1项 - 静态工厂方法

25

我正在阅读 Joshua Bloch 的 Effective Java,有关第一条 Static Factory Method 的问题。

引用[Bloch, p.7]

接口不能有静态方法, 因此按照惯例,接口的 静态工厂方法被放在一个无法实例化的类中, 这个类被命名为 Types。例如, Java 集合框架提供了不可修改的集合、同步的集合等。 几乎所有这些实现都通过一个非实例化类 (java.util.Collections)的静态工厂方法进行导出。 返回对象的类都是非公共的。

好的。当查看源代码时,我看到了 java.util.Collection 接口和带私有构造函数(即无法实例化的类)的 java.util.Collections 类。我发现无法实例化的类 Collections 具有所有静态方法,就像 Bloch 所说的那样。但是我没有看出两个类之间的联系,就像 Bloch 所说的那样:

接口不能有静态方法,因此按照惯例,接口的静态工厂方法被放在一个无法实例化的类中,这个类被命名为 Types。

  1. 有人可以给我指出其中的显而易见之处吗?

  2. 他说返回对象的类都是非公共的是什么意思?

这里是获取 Java 源代码的地方:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Collection.java?av=f


1
参见[Bloch,Item 1](http://drdobbs.com/java/208403883?pgno=1)。 - trashgod
3
对于我来说,与Bloch有关的任何事情都是默认加一分的。 - Thufir
6个回答

30
  1. 接口无法拥有静态方法,因此按照惯例,用于接口 Type 的静态工厂方法应该放在一个不能实例化的类 Types 中。

    重点在于“Type[s]”中使用了复数形式的 's'。 因此,如果您的接口名称为 Foo,并且要创建名为 MyFoo 的某些实现,则根据惯例,用于实例化的工厂方法应命名为 Foos

  2. 返回对象的类都不是公共类

    这意味着从工厂方法返回的对象的类具有 private 或 default 可见性修饰符,例如 private class MyFoo{},因此它们只能通过其工厂方法进行实例化。由于您无法使用 new 运算符从私有内部或包私有类的外部构造对象(反射除外),因此它们只能通过工厂方法进行实例化。

例如:

 public interface Foo{ //interface without plural 's' (question 1)
     public void bar();
 }
 public abstract class Foos(){ // abstract factory with plural 's' (question 1)
    public static Foo createFoo(){
        return new MyFoo();
    }
    private class MyFoo implements Foo{ // a non visible implementation (question 2)
       public void bar(){}
    }
 }

1
关于 1 的说明很好。我现在理解得更好了。非常感谢。至于2: 将返回对象设置为私有类的实例的好处是什么? - Thang Pham
4
只要API用户没有自己使用new关键字构造对象,你就可以在不被API用户察觉的情况下切换实现方式。因此,如果你需要重新实现某些功能,你可以创建一个新的版本发布而无需进行任何API更改。 - fasseg
1
嗯,我发现我需要给内部类 MyFoo 添加 'static' 修饰符才能让它编译通过...否则,我会得到 Foos 没有封闭实例的错误。我做错了什么吗? - Mats_SX
@Mats_SX:我刚刚在那里读到了一些东西。http://javarevisited.blogspot.sg/2012/02/why-non-static-variable-cannot-be.html基本上归结为,在内部MyFoo类中,您必须实例化一个Foos的实例并引用它。 - MacD
一个很好的例子是Guava中的Lists类,它包含了用于创建List实例的静态工厂函数。例如,您可以使用以下代码创建一个字符串列表:List<String> list = Lists.newArrayList("first", "second", "third") - pedromanoel

4
假设你有一个名为 List 的接口,你想使用静态工厂方法来创建不同类型的列表。由于接口是接口,无法在 List 接口中定义静态工厂方法。因此,您需要一个类来返回实现 List 接口的类的实例。
public class ListFactory{
  private ListFactory(){}
  public static List makeArrayList(){...}
  public static List makeLinkedList(){...}
  public static List makeCrazyList(){...}
}

您无法做到这一点

public interface List{
   public static List makeArrayList();
   public static List makeLinkedList();
   public static List makeCrazyList();
}

由于List是接口。


从Java 8开始,接口可以包含静态方法的限制被取消了。 - Mohdroid

2
public interface Foo
    static public class Factory
        public Foo create(){..}

Foo foo = Foo.Factory.create();

2
Collections.unmodifiableList(...)为例。它返回一个List的实现。但是实现类的名称是无关紧要的。此外,该类只能通过静态工厂方法构造。

1

1) 我不明白你在这里的问题。 Collection 是接口,而 Collections 有一些工厂方法,比如 emptyList

2) 例如,由 Collection.emptyList 返回的 List 实例是一个实现了 List 接口的私有类的实例。


我还有一个问题要问你。对于2,将返回对象设置为私有类的实例有什么好处? - Thang Pham

1
这句话的意思是,Collections(以及类似它的其他类)中静态工厂方法的返回类型是接口类型(例如List),而不是特定实现类(例如java.util.Collections.UnmodifiableList),因为后者对用户来说并不可见。如果公开使用特定实现类,会使事情变得更加复杂,并增加API的大小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接