抽象类中的私有构造函数
Posted
技术标签:
【中文标题】抽象类中的私有构造函数【英文标题】:Private constructor in abstract class 【发布时间】:2012-07-22 11:05:46 【问题描述】:在 Java 中,在抽象类中使用私有构造函数的目的是什么?
在评论中我得到了这个问题,我很好奇,在什么情况下我们需要以这种方式使用构造函数?
我认为它可以与抽象类中的另一个构造函数配对使用,但这非常琐碎。也可用于构造超越抽象类的静态内部类。
也许还有更优雅的用法?
【问题讨论】:
我没有在这个问题上投票,但我怀疑投反对票的人反对“也许有更优雅的用法” - 这很宽泛,听起来很修辞。 问题是:"在Java中,抽象类中使用私有构造函数的目的是什么?"。这对我来说似乎已经足够具体了。 谢谢回复,可能你是对的。 当你在面试中遇到这样的问题时,通常没有一个正确的答案。面试官可能只是想测试你的思维过程。 如果您将最初的问题更改为“在 Java 中,在抽象类中使用私有无参数构造函数的目的是什么?”那么有一个很好的答案。它是强制子类提供与包含所需参数的抽象类中另一个构造函数的签名相匹配的构造函数。编译器将强制执行这有助于防止子类中的错误。如果问题重新打开,我可以提供详细的示例代码。 【参考方案1】:只有私有构造函数的最终类是单例和多例使用的设计。
只有私有构造函数的抽象类是我见过的防止类被实例化的唯一方法。我已经看到它用于创建实用程序类(只有静态方法和/或成员)。
至于设置用户期望,我看到https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html 声明“抽象类无法实例化,但它们可以子类化。”我注意到它并没有说明任何期望它们被子类化的意图。
然而,我还注意到查看一些 Java 源代码,我发现使用了以下设计(都不是只有私有构造函数的抽象类):
具有私有构造函数的最终实用程序类 http://developer.classpath.org/doc/java/lang/Math-source.html http://developer.classpath.org/doc/java/lang/System-source.html 具有私有构造函数的最终实用程序类 抛出异常 http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Objects.java 既不是抽象的也不是最终的实用程序类具有私有构造函数 http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/ArrayPrefixHelpers.java http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Arrays.java https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Collections.java http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/FormattableFlags.java 看起来像一个实用程序,但显然可以实例化(没有私有 构造函数) http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/ArraysParallelSortHelpers.java【讨论】:
【参考方案2】:抽象类中的私有构造函数也可以用于sealed
类(如在 Scala 或 Kotlin 等中)。由于您仍然可以从抽象类中提供子类,但外人无法扩展/实现(正如@Marko Topolnik 回答的那样)。
看起来我们会得到sealed interface
来更干净地支持这一点。见https://openjdk.java.net/jeps/8222777
【讨论】:
【参考方案3】:如果private
构造函数是类的only构造函数,那么原因很明确:防止子类化。某些类仅用作静态字段/方法的持有者,并且不希望被实例化或子类化。请注意,abstract
修饰符在这种情况下是多余的——不管有没有它,都不可能实例化。正如@JB Nizet 在下面指出的那样,abstract
修饰符也是不好的做法,因为它向类的客户端发送了错误的信号。该类实际上应该是final
。
还有另一个用例,但非常少见:您可以拥有一个 abstract class
,只有 private
构造函数包含自己的子类作为嵌套类。这个习惯用法确保那些嵌套类是唯一的子类。事实上,Java 中的enum
s 就是使用这个习语。
如果周围还有其他构造函数,那么private
构造函数确实没有什么特别之处。它在abstract
类中使用,就像在任何其他类中一样。
【讨论】:
我会说在这种情况下使用抽象不仅是多余的,而且是令人困惑的。根据定义,抽象类意味着要被子类化。将类抽象化,但不能子类化,表明对抽象的用途缺乏了解。 @JBNizet 不一定是缺乏理解,但可能是缺乏注意不要让您的客户感到困惑。顺便说一句,请注意我刚刚添加的第二段。这为abstract
类中的private
ctor 提供了完美的动力。【参考方案4】:
如前所述,用作通用的仅供内部使用的构造函数。
无论是抽象还是非抽象,在仅包含静态公共方法 [辅助方法] 的类上声明私有默认构造函数以防止实例化该类的情况并不少见。
【讨论】:
@Thilo - 我同意 (3) 不适用,我被带走了 - 这将是 (1) 的扩展。 4 有资格澄清它并不直接适用于特定问题。【参考方案5】:有时,默认的无参数构造函数是私有的,并提供另一个接受参数的构造函数。然后,此构造函数可能会调用其他私有构造函数。这会强制实现提供这些参数,这可能会确保始终初始化某些变量,尽管这不是常见的做法(根据我的经验)。如果这是要求,你最好检查你的变量并抛出一个IllegalArgumentExeption
,解释为什么需要初始化变量。
如果您使用仅 私有构造函数创建抽象类,则该类实际上是无用的,因为永远无法创建实例。如果打算创建一个只有静态方法的实用程序类(如java.lang
包中的Math
类),私有构造函数是可以接受的,但是应该将类标记为final,因为将类标记为抽象意味着类将被扩展。
【讨论】:
但是,如果您只显式提供一个构造函数,则没有默认的无参数构造函数,因此这并不是将其设为私有的真正理由。【参考方案6】:我唯一能想到的就是重用其他(受保护的)构造函数共享的公共代码。然后他们可以在第一行调用私有构造函数。
【讨论】:
【参考方案7】:不可能有其他优雅的用途
【讨论】:
以上是关于抽象类中的私有构造函数的主要内容,如果未能解决你的问题,请参考以下文章