我在一次采访中被问到这个问题。我有一个基类(比如 class A
),然后是两个子类B
和C
. 现在我无法控制 B 和 C 的构造函数(那些构造函数不能是 private ,必须是 public )但要求是 and 的每个实例都B
应该C
是单例。我怎样才能做到这一点?
2 回答
我想我会在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()
orsuper()
调用周围放置 try / catch,因此一旦抛出异常,子类的构造函数就永远无法正常返回。
我会说这是一个相当难的面试问题......
@emory 评论:
如果 B 和 C 不是最终的怎么办?然后我可以创建类 B1、B2、C1、C2 等。
这里的问题(如果算作问题的话)是 B1 和 B2 实例也是 B 实例,这意味着 B 实例不再是单例……取决于您希望实现的单例的定义。
我可以看到几种处理方法:
您可以反思性地测试子类修饰符,看看这些类是否是最终类,并拒绝创建非最终类的实例......以防万一。
您可以将 替换
HashSet<Class>
为List<Class>
。然后每次A
调用构造函数时,它都会遍历调用elem.isAssignableFrom(c)
每个元素类的列表。如果任何调用返回 true,则违反(严格)单例不变量,因此应抛出异常。
逻辑可能需要根据您尝试强制执行的单例模型进行调整,但通用解决方案适用:记录类并检查/比较新类与以前的类。
我正在为 B 级展示它
尽管您可以使用Double checked locking
, 并synchronized on method
做到这一点..我向您展示了一种快速而肮脏的方法...
public class B {
private static B b = new B();
private B() {}
public static B getInstance() {
return b;
}
}