【问题标题】:Can a class have both public and private constructor?一个类可以同时具有公共和私有构造函数吗?
【发布时间】:2013-07-05 09:01:28
【问题描述】:

我遇到了一个场景,我需要一个公共和一个私有构造函数。需要一个私有构造函数来设置类型为私有内部类的私有字段。 这是鼓励还是气馁?对于下面列出的场景,还有什么更好的解决方案?

请阅读评论,它更有意义地支持我的问题。谢谢,

public class CopyTree { 
    private TreeNode root;

    public Copytree() { }

    //  private CopyTree(TreeNode root) { this.root = root; }

    private static class TreeNode {
       TreeNode left;
        TreeNode right;
        Integer element;
        TreeNode(TreeNode left, TreeNode right, Integer element) {
            this.left = left;
            this.right = right;
            this.element = element;
    }
}

public CopyTree copyTree() {
    CopyTree ct = new CopyTree();
    ct.root = copyTree(root);  // <---- QUESTION:  Any cleaner solution ?? 
    // cleaner solution appears to be a private constructor
    // eg: CopyTree ct = new CopyTree(copyTree(root));  But can public and private constructor     coexist together ?
    return ct;
}

private TreeNode copyTree(TreeNode binaryTree) {
    TreeNode copy = null;
    if (binaryTree != null) {
        copy = new TreeNode(null, null, binaryTree.element);
        copy.left =  copyTree(binaryTree.left); 
        copy.right = copyTree(binaryTree.right);
    }
    return copy;
}

【问题讨论】:

标签: java private public


【解决方案1】:

一个类可以同时拥有公有构造函数和私有构造函数吗?

是的,有可能。

需要一个私有构造函数来设置类型为私有内部类的私有字段。这是鼓励还是不鼓励?

视情况而定。您是否希望其他类初始化您的对象的状态。在这里,我认为您已经创建了类 CopyTree 来返回作为私有类的 Tree 的副本。因此 TreeNode 类将被封装,因此您可以选择使用私有构造函数捕获习语。

下面列出的场景有什么更好的解决方案?

在我看来,私有构造函数捕获习语是更好的解决方案。

更多信息:

你可以搜索private constructor capture idiom

Java Puzzlers的解决方案53中给出了一个例子:

谜题 53:做你的事

现在轮到你编写一些代码了。假设您有一个名为 Thing 的库类,其唯一的构造函数采用 int 参数:

public class Thing {    
    public Thing(int i) { ... }
        ...
    }

Thing 实例无法获取其构造函数参数的值。因为 Thing 是一个库类,您无法访问其内部,也无法修改它。 假设您要编写一个名为 MyThing 的子类,其构造函数通过调用 SomeOtherClass.func() 方法计算超类构造函数的参数。此方法返回的值会因调用而发生不可预测的变化。最后,假设您希望将传递给超类构造函数的值存储在子类的最终实例字段中以供将来使用。这是你自然会写的代码:

public class MyThing extends Thing {
    private final int arg;
    public MyThing() {
        super(arg = SomeOtherClass.func());
        ...
    }
    ...
}

很遗憾,这是不合法的。如果您尝试编译它,您将收到一条类似于以下内容的错误消息:

MyThing.java:
  can't reference arg before supertype constructor has been called
        super(arg = SomeOtherClass.func());
                 ^

如何重写 MyThing 以达到预期的效果? MyThing() 构造函数必须是线程安全的:多个线程可以同时调用它。

解决方案 53:做你的事

您可以尝试在调用 Thing 构造函数之前将调用 SomeOtherClass.func() 的结果存储在静态字段中。这个解决方案可行但很尴尬。为了实现线程安全,您必须同步对隐藏值的访问,这需要难以想象的扭曲。通过使用线程局部静态字段 (java.util.ThreadLocal) 可以避免其中一些扭曲,但存在更好的解决方案。 首选的解决方案本质上是线程安全且优雅的。它涉及在 MyThing 中使用第二个私有构造函数:

public class MyThing extends Thing {
    private final int arg;

    public MyThing() {
        this(SomeOtherClass.func());
    }

    private MyThing(int i) {
        super(i);
        arg = i;
   }
}

此解决方案使用备用构造函数调用。此功能允许类中的一个构造函数链接到同一类中的另一个构造函数。在这种情况下,MyThing() 链接到私有构造函数 MyThing(int),该构造函数执行所需的实例初始化。在私有构造函数中,表达式 SomeOtherClass.func() 的值已经被捕获到参数 i 中,并且可以在超类构造函数返回后存储在 final 字段 param 中。

【讨论】:

    【解决方案2】:

    从您注释掉的代码来看,您已经尝试过它并且它有效。因此,我只能确认您的发现:是的,私有构造函数可以与公共构造函数共存,是的,这似乎是一个更好的解决方案,因为在接收到对新对象的引用之前完成了更多的初始化。

    【讨论】:

      【解决方案3】:

      您只能通过以下方式使用私有和公共构造函数。但是你不能将两者都用于无参数构造函数或相同的参数类型。

      public class MyClass {
      private MyClass(){
      
      }
      public MyClass(int i){
      
      }
      }
      

      【讨论】:

        【解决方案4】:

        可以防止类被调用者使用 Private 显式实例化 构造函数。

        私有构造函数在哪里有用,

        • 仅包含静态实用方法的类
        • 只包含常量的类
        • 类型安全枚举
        • 单身人士

        它们被证明是不利的(不限于以下几点。列表可能会增加)

        • 没有公共或受保护构造函数的类不能 子类化。
        • 它们不易与其他静态方法区分开来。

        【讨论】:

          猜你喜欢
          • 2017-04-30
          • 2011-02-08
          • 1970-01-01
          • 1970-01-01
          • 2010-10-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-26
          相关资源
          最近更新 更多