英文:
How do I create only one object of parent and child
问题
设计一个类,以便只能创建该类的一个实例以及其所有子类的一个实例。为了澄清:假设A是这样一个类,B是从A派生的。B除了扩展A之外,没有任何特殊代码。
class A {
private static A instance = null;
protected A() {
// Constructor code of class A
}
public static A getInstance() {
if (instance == null) {
instance = new A();
}
return instance;
}
}
public class B extends A {
private static B bInstance = null;
protected B() {
// Constructor code of class B
}
public static B getInstance() {
if (bInstance == null) {
bInstance = new B();
}
return bInstance;
}
public static void main(String[] args) {
A a1 = A.getInstance(); // works fine
A a2 = A.getInstance(); // works fine
B b1 = B.getInstance(); // works fine as this is the first instance of B
B b2 = B.getInstance(); // causes an exception.
}
}
但是我们需要使用 new 关键字创建对象。
我尝试在父类中定义静态值,但这并没有帮助。
英文:
Design a class such that only one instance of the class and any of its sub-classes can be created. To clarify: Assume A is such a class and B is derived from A. B does not have any special code apart from extending A.
class A {
// code of class A
}
public class B extends A{
public static void main(String[] args)
{
new A(); // works fine.
new A(); // causes an exception
new B(); // works fine as this is the first instance of B
new B(); // causes an exception.
}
}
But we need to create the object with the new keyword.
I try in static value define in parent but this does not help.
答案1
得分: 4
class A {
private static Map<Class<? extends A>, A> instances = new HashMap<>();
public A() {
synchronized (A.class) {
if (instances.containsKey(this.getClass())) {
throw new IllegalStateException();
}
instances.put(getClass(), this);
}
}
// code of class A
}
public class B extends A {
public static void main(String[] args) {
new A(); // 正常运行。
new A(); // 抛出异常。
new B(); // 正常运行,因为这是 B 的第一个实例。
new B(); // 抛出异常。
}
}
当你创建一个 `A` 类的实例或者 `A` 的任何子类的实例时,都会调用 `A` 类的构造函数。
构造函数会检查 `Map` `instances` 中是否存在 `A` 类的实例。
如果当前类的实例已存在,则会抛出异常。
如果当前类的实例不存在(没有抛出异常),则将当前对象保存到 `instances` 中。
`Map` 是静态的,因此相同的 `Map` 会在所有实例之间共享(如果每个 `A` 类的实例都有自己的 `Map`,那么这种方法显然是行不通的)。
`synchronized` 块确保通过锁定 `A` 类的 `Class` 对象来保证对映射的访问是线程安全的。锁定当前的 `Class` 对象是不安全的,因为在创建新对象时,`HashMap` 的 `put` 方法不是线程安全的。
你还可以锁定 `instances` 或者像 [这个答案](https://stackoverflow.com/a/63898225/10871900) 中由 [oleg.cherednik](https://stackoverflow.com/users/3461397/oleg-cherednik) 描述的那样,使用一个 `Class` 的 `Set`。
英文:
class A {
private static Map<Class<? extends A>, A> instances = new HashMap<>();
public A() {
synchronized (A.class) {
if (instances.containsKey(this.getClass())) {
throw new IllegalStateException();
}
instances.put(getClass(), this);
}
}
// code of class A
}
public class B extends A {
public static void main(String[] args) {
new A(); // works fine.
new A(); // causes an exception
new B(); // works fine as this is the first instance of B
new B(); // causes an exception.
}
}
When you create an instance of A
or any subclass of A
, the constructor of A
it is called.
The constructor checks if an instance of A
is present in the Map
instances
.
If an instance of the current class exists, an exception is thrown.
If no instance of the current class exists (when no exception is thrown), the current object is saved to instances
.
The Map
is static
so the same Map
is used across all instances (It obviously would not work if every instance of A
had its own Map
).
The synchronized
block makes sure that the map access is thread safe by locking the Class
object of A
. Locking the current Class
object is not thread safe as the put
method of HashMap
is not thread safe when creating new objects.
You can also lock instances
or use a Set
of Class
es as described in this answer from oleg.cherednik.
答案2
得分: 1
class A {
private static final Set<Class<? extends A>> INSTANCES = new HashSet<>();
public A() {
synchronized (INSTANCES) {
if (INSTANCES.contains(getClass()))
throw new RuntimeException("duplication: " + getClass().getSimpleName());
INSTANCES.add(getClass());
}
}
// code of class A
}
class B extends A {
public static void main(String[] args) {
new A(); // works fine.
new A(); // causes an exception
new B(); // works fine as this is the first instance of B
new B(); // causes an exception.
}
}
英文:
class A {
private static final Set<Class<? extends A>> INSTANCES = new HashSet<>();
public A() {
synchronized (INSTANCES) {
if (INSTANCES.contains(getClass()))
throw new RuntimeException("duplication: " + getClass().getSimpleName());
INSTANCES.add(getClass());
}
}
// code of class A
}
class B extends A {
public static void main(String[] args) {
new A(); // works fine.
new A(); // causes an exception
new B(); // works fine as this is the first instance of B
new B(); // causes an exception.
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论