在Java中,似乎公共构造函数总是一个糟糕的编码实践

时间:2012-10-24 16:47:29

标签: java coding-style

  

可能重复:
  Consider providing static factory methods insteads of constructors

这可能是一个有争议的问题,可能不适合这个论坛(所以如果你选择关闭这个问题,我不会被侮辱)。

似乎考虑到Java的当前功能,没有理由让构造函数public ... 永远。友好,privateprotected可以,但public没有。

似乎提供创建对象的public static方法几乎总是更好的主意。每个Java Bean序列化技术(JAXB,Jackson,Spring等......)都可以调用受保护或私有的无参数构造函数。

我的问题是:

  • 我从未见过这种做法在任何地方被下令或写下来?也许布洛赫提到它,但我不拥有是书。
  • 我错过了除了可能不是超级DRY之外的用例吗?

编辑:我解释为什么静态方法更好。

0.1。对于一个你得到更好的类型推断。例如,参见Guava的http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained

0.2。作为该类的设计者,您可以稍后使用静态方法更改返回的内容。

0.3。处理构造函数继承很痛苦,特别是如果你必须预先计算某些东西。  0.4。更多理由:https://stackoverflow.com/a/3852556/318174

我应该发布这是针对公共API的代码。我经常违反各种规则(比如使用直接现场访问)进行单元测试,方便,因为我很懒。因此,当我的意思是永远时,我的意思是你将它释放到野外。

4 个答案:

答案 0 :(得分:7)

如果以下是该方法的外观

public class MyClass {

    private MyClass() { }

    public static MyClass getInstance() {
        return new MyClass();
    }
}

然后更愿意拥有一个公共的无参数构造函数。

如果我必须调用这样的方法,它会让我觉得它正在做一些事情来构建对象,但实际上并非如此。

我不认为有一个方法除了调用构造函数本身之外什么都不做。

答案 1 :(得分:3)

简而言之,静态工厂很酷,它们有自己的用途,但是有些API会陷入在任何地方使用它们的陷阱,即使它们没有增加价值而只是增加复杂性。

静态工厂在Java中运行良好的一个例子是EnumSet,其中有许多命名的构建器无法自然地实现为重载构造函数。

e.g。即使它们具有相同的参数,它们也不会做同样的事情。

EnumSet.of(E1, E3);
EnumSet.range(E1, E3);

EnumSet也会根据枚举中的元素数返回两个不同的实现。

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

不幸的是,EnumMap的功能并不相同,所以只有一个实现。


  

0.1。对于一个你得到更好的类型推断。例如参见Guava的

所以Guava有像

这样的方法
List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

在Java 7中只是

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();
Map<KeyType, LongishValueType> map = new LinkedHashMap<>();

更短,您不需要学习任何新方法和Java 6,如果您不需要对类型进行双重检查,则可以执行以下操作。

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList();
Map<KeyType, LongishValueType> map = new LinkedHashMap();

在番石榴你有

Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

其中内置方法是

Set<Type> copySet = new HashSet<String>(elements);
List<String> theseElements = Arrays.asList("alpha", "beta", "gamma");

如果从HashSet中删除<String>,则会丢失类型安全性,但如果最好的IDE会自动为您完成此代码,那么您实际上不会保存任何键入内容。

  

0.2。作为该类的设计者,您可以稍后使用静态方法更改返回的内容。

我会说YAGNI,在实践中很难透明地显着改变实现。在不必重新构建或重新测试代码的情况下,您很可能无法完全向后兼容。

  

0.3。处理构造函数继承很痛苦,特别是如果你必须预先计算一些东西。

这是事实,但很少见。对于这种情况,我通常有一个复杂构造的构建器类,单独的因子方法不能解决问题。


值得考虑的是,Java库中的大多数类都使用构造函数而不是静态工厂。我能想到的唯一可以使用构造函数的类但后来改为使用静态工厂的类是自动装箱包装类。知道哪种工厂方法要被语言隐藏的复杂性。

  

似乎鉴于Java的当前功能,没有理由让构造者公开......永远。友好,私密,受保护,但公众没有。

仅仅因为你可以做某事并不能让它成为好主意。

例如,您可以将所有类,方法和变量设置为1或2个字符(您永远不需要使用3个字母或更长的名称,有些人认为这样更好一些)但是这并不是好主意。

顺便说一下,如果查看常见的UNIX命令,很多都是两个字符长。 ;)

  

似乎提供一个用于创建对象的公共静态方法几乎总是更好的主意。

除非你更喜欢简单,不要让你的代码不必要地复杂化。

  

我从未见过这种做法在任何地方被命令或写下来了吗?

我也没有。可能是因为它不是一个好主意。 IMHO。

  

是否有一个用例,而不是我错过的超级DRY?

你没有说明这么做的理由,这就是我不能这样做的理由。 ;)

答案 2 :(得分:2)

我不这么认为。需要构造函数来创建对象。构造函数保证该对象确实创建了对于可返回null的工厂方法不正确。

构造函数或工厂方法之间的选择取决于具体的用例。有时构造函数更好,有时工厂方法有优势。

我宁愿说在类本身中使用静态工厂方法在大多数情况下都是不好的做法。恕我直言分类本身及其工厂是更好的解决方案。

我不认为所有框架都支持工厂方法。 Spring和JAXB确实支持,但例如HttpServletHttpFilter,EJB,Applet等必须具有公共默认构造函数。即使在春季使用bean的情况下,构造函数比需要工厂方法进行实例化的bean更方便。

答案 3 :(得分:0)

我可以想到公共构造函数的一个用例。如果使用注释处理框架,则需要实现Processor。编译器将使用反射来创建处理器的实例。为了使系统工作,您的实现类应该是公共的,而不是通用的,并且具有公共的无参数构造函数。

因此,公共构造函数可能对使用反射的框架有用。