如何创建父类和子类的唯一对象

huangapple go评论72阅读模式
英文:

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&lt;Class&lt;? extends A&gt;, A&gt; instances = new HashMap&lt;&gt;();
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 Classes 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&lt;Class&lt;? extends A&gt;&gt; INSTANCES = new HashSet&lt;&gt;();
public A() {
synchronized (INSTANCES) {
if (INSTANCES.contains(getClass()))
throw new RuntimeException(&quot;duplication: &quot; + 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.
}
}

huangapple
  • 本文由 发表于 2020年9月15日 12:19:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63895013.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定