如何创建派生类的单例?

问题描述:

我在采访中被问到这个问题。我有一个基类(说类A),然后有两个子类BC。现在我无法控制B和C的构造函数(这些构造函数不能是私有的,必须公开),但要求是BC的每个实例都应该是单例。我怎样才能做到这一点?如何创建派生类的单例?

+2

这是什么意思,没有控制?你不能改变它的代码(你不需要)?或者你不能改变它的知名度? – Vitaliy 2012-08-14 05:45:23

+0

你能定义'不能控制构造函数吗? – 2012-08-14 06:03:41

我想我会在构造函数A中这样做。获取它调用this.getClass(),并使用它在私有HashSet中进行查找。如果您遇到了问题,那么此类的一个实例以前就已经创建好了,并且会抛出异常。

public abstract class A { 
    private static HashSet<Class<?>> classes = new HashSet<Class<?>>(); 

    public A() { 
     synchronized (classes) { 
      Class<?> c = this.getClass(); 
      if (classes.contains(c)) { 
       throw NotSingletonException("Class " + c + " is not singleton"); 
      } 
      classes.add(c); 
     } 
    } 
} 

如果您安排A的所有构造函数都这样做,那么子类无法避免检查。而且由于JLS不会让你尝试围绕this()super()调用进行尝试/捕获,所以一旦抛出异常,子类的构造函数就无法正常返回。


我会说,这是一个相当困难的面试问题...


@emory评论:

如果B和C是什么不是最终的?然后,我可以创建类B1,B2,C1,C2等

这里的问题(如果它计数为有问题)是,B1和B2实例也是乙实例,并且这意味着,乙实例不再是单身人士...取决于你渴望实施的单身人士的定义。

我可以看到一对夫妇的处理这个问题的方式:

  • 您可以反射性地测试子类修饰符看到,如果类是final的,拒绝创建非final类的实例...以防万一。您可以用List<Class>代替HashSet<Class>。然后每次调用构造函数A时,都会遍历每个元素类调用elem.isAssignableFrom(c)的列表。如果任何调用返回true,则(严格)单例不变量被违反,因此应抛出异常。

的逻辑可能需要根据的单岬您要执行的模型进行调整,但总的解决方案适用:记录类和检查/与以前的比较新的类。

+0

如果B和C不是最终的?然后我可以创建类B1,B2,C1,C2等。我想你可以通过在'c'是A之前连续获取'c'超类来解决这个问题(如果它算作一个问题)。 – emory 2012-08-14 07:24:35

我展示它的B类

虽然你可以使用Double checked locking,并synchronized on method做到这一点..我向您展示这样做的一个快速和肮脏的方式...

public class B { 

    private static B b = new B(); 

    private B() {} 

    public static B getInstance() { 
      return b; 
    } 
} 
+0

查看编辑的问题。 B的构造函数不能是私有的。 – Geek 2012-08-14 06:17:42