创建继承层次结构中每个类的单个实例

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

Creating single instances of each class in the inheritance hierarchy

问题

以下是翻译的代码部分:

假设有三个类,分别为A,BC,其中B扩展AC扩展B

要求是客户端代码应该能够只能成功调用每个类的构造函数一次。尝试两次调用构造函数时,应该抛出异常

如果不允许在子类中重复代码,我该如何在Java中实现这一点?

示例:

public class A {
    private static A instance;

    public A() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }
}

public class B extends A {
    private static B instance;

    public B() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }
}

public class C extends B {
    private static C instance;
    public C() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }

}

public class Driver {
    public static void main(String[] args) {
        A a1 = new A();
        B b1 = new B(); // 抛出IllegalArgumentException,不应该抛出
    }
}

我尝试过的方法:

保持各自类类型的私有静态引用,最初设置为null。
在构造函数块中,我添加了一个空检查,以将this引用分配给静态引用。但由于我无法避免重复的代码,所以没有成功。

要求:

// 这应该正常工作

A a1 = new A();
B b1 = new B();
C c1 = new C();

---------------

// 这应该抛出运行时异常

A a1 = new A();
A a2 = new A();
B b1 = new B();


---------------
// 这应该抛出运行时异常

A a1 = new A();
B b1 = new B();
B b2 = new B();

---------------

// 这应该抛出运行时异常

A a1 = new A();
B b1 = new B();
C c1 = new C();
C c2 = new C();


我希望我的需求是清楚的。

英文:

Suppose there are three classes namely A, B and C such that B extends A, C extends B.

Requirement is that client code should be able to call the constructors of each class only once successfully. Upon trying to call constructor twice it should throw an exception.

How can I implement this in Java if duplication of code is not permitted in the child classes?

Example :

public class A {
    private static A instance;

    public A() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }
}

public class B extends A {
    private static B instance;

    public B() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }
}

public class C extends B {
    private static C instance;
    public C() {
        if (instance != null) {
            throw new IllegalArgumentException();
        }
        instance = this;
    }

}

public class Driver {
    public static void main(String[] args) {
        A a1 = new A();
        B b1 = new B(); //throwing IllegalArgumentException, it should not throw
    }
}


Things I tried.

Maintaining a private static reference of the respective class type which is initially set as null.
In the constructor block I added a null check to assign this reference to the static reference. Did not work as I could not avoid duplicating code.

Requirement

//this should work fine

A a1 = new A();
B b1 = new B();
C c1 = new C();

---------------

//this should throw runtime exception

A a1 = new A();
A a2 = new A();
B b1 = new B();


---------------
//this should throw runtime exception

A a1 = new A();
B b1 = new B();
B b2 = new B();

---------------

//this should throw runtime exception

A a1 = new A();
B b1 = new B();
C c1 = new C();
C c2 = new C();


I hope I am clear with the requirements

答案1

得分: 2

问题

我们已经确定子类隐式调用了超级构造函数,而超级构造函数则抛出异常。

1. 单例 / 工厂方法解决方案

您想要实现的是描述为单例设计模式的概念。它在概念上需要静态成员。
静态字段和/或方法(如果要使用工厂方法)不会被继承,因此不可避免地会有重复操作静态字段的代码。

应该使用重复的空值检查和存储静态实例技术,这是广泛使用和被接受的。代码重复的量很小,不应该担心它。

已编辑,关于隐式超级构造函数调用:您可以使用条件(如A中的条件)或工厂方法(如B中的方法)。

public class A {
	private static A instance;
	
    public A() {
		if (this.getClass() == A.class) {
			if (instance != null) {
				throw new IllegalArgumentException();
			}
			instance = this;
		}
	}
}

public class B extends A {
	private static B instance;

	private B() { }

    public static B getInstance() {
		if (instance != null) {
			throw new IllegalArgumentException();
		}
		return instance = new B();
	}
}

public class C extends B {
	private static C instance;
		
	public C() {
		// check if (this.getClass() == B.class) when someone extends C
		if (instance != null) {
			throw new IllegalArgumentException();
		}
		instance = this;
	}
}

public static void main(String[] args) {
    A a1 = new A();
	B b1 = B.getInstance();
	C c1 = new C();
}

或者,您可以声明一个private构造函数并拥有一个static工厂方法。如果您在多线程环境中,请注意同步


2. 收集所有实例在顶层父类中

另一个不太常见的解决方案是在顶级父类中收集所有Class&lt;&gt;实例,并在构造函数中检查重复性。这不是一个<strike>好的</strike>常规解决方案。 单例模式是常见的。

public class TopClass {
	private static final Set&lt;Class&lt;? extends TopClass&gt;&gt; instances = new HashSet&lt;&gt;();

	public TopClass() {
		if (instances.contains(this.getClass())) {
			throw new IllegalArgumentException();
		}
		instances.add(this.getClass());
	}
}

public class SubClass extends TopClass {}
public class AnotherClass extends SubClass {}

这样,您限制了所有未来的子类只能实例化一次。这是有限制的,但是是更少的代码行。

英文:

Problem

We have established that the subclass implicitly calls the super constructor, which in return throws.

1. Singleton / Factory Method solution

What you want to achieve is described as a Singleton design pattern. It conceptually requires a static members.
Static fields and/or methods (if you want to use a factory method) are not inherited, so duplication of the code which manipulates the static field is inevitable.

You should use the duplicated null-check & store static instance technique, it is widely used and accepted. The amount of code duplication is minimal and you should not be scared of it.

EDITED as to implicit super constructor call: You can use a condition (as in A) or a factory method (as in B).

public class A {
	private static A instance;
	
    public A() {
		if (this.getClass() == A.class) {
			if (instance != null) {
				throw new IllegalArgumentException();
			}
			instance = this;
		}
	}
}

public class B extends A {
	private static B instance;

	private B() { }

    public static B getInstance() {
		if (instance != null) {
			throw new IllegalArgumentException();
		}
		return instance = new B();
	}
}

public class C extends B {
	private static C instance;
		
	public C() {
		// check if (this.getClass() == B.class) when someone extends C
		if (instance != null) {
			throw new IllegalArgumentException();
		}
		instance = this;
	}
}

public static void main(String[] args) {
    A a1 = new A();
	B b1 = B.getInstance();
	C c1 = new C();
}

Alternatively, you can declare a private constructor and have a static Factory Method. If you are in a multi-threaded environment, pay attention to synchronisation.


2. Collecting all instances in the top-most parent

An other obscure solution would be to collect all Class&lt;&gt; instances in the top parent and check for duplicity in the constructor. This is not a <strike>good</strike> conventional solution. The singelton pattern is usual.

public class TopClass {
	private static final Set&lt;Class&lt;? extends TopClass&gt;&gt; instances = new HashSet&lt;&gt;();

	public TopClass() {
		if (instances.contains(this.getClass())) {
			throw new IllegalArgumentException();
		}
		instances.add(this.getClass());
	}
}

public class SubClass extends TopClass {}
public class AnotherClass extends SubClass {}

This way you limit all future subclasses to be instantiated only once. It is limiting, but yes - less lines of code.

答案2

得分: 0

class A {
private static final Set<Class<? extends A>> calledInitializations = new HashSet<>();

A() {
    checkForSecondInit(A.class);
}

protected void checkForSecondInit(Class<? extends A> clazz) {
    if (isNotInSuperClassOf(clazz)) {
        if (calledInitializations.contains(clazz)) {
            throw new RuntimeException("Second init in " + clazz.getName());
        } else {
            calledInitializations.add(clazz);
        }
    }
}

private boolean isNotInSuperClassOf(Class<? extends A> clazz) {
    return getClass() == clazz;
}

}

class B extends A {
B() {
checkForSecondInit(B.class);
}
}

class C extends B {
C() {
checkForSecondInit(C.class);
}
}

英文:

I suppose you can make a Set of already initialized classes and check it every time you construct a new one. It allows create one instance of A, one instance of B and one instance of C.

class A {
    private static final Set&lt;Class&lt;? extends A&gt;&gt; calledInitializations = new HashSet&lt;&gt;();

    A() {
        checkForSecondInit(A.class);
    }

    protected void checkForSecondInit(Class&lt;? extends A&gt; clazz) {
        if (isNotInSuperClassOf(clazz)) {
            if (calledInitializations.contains(clazz)) {
                throw new RuntimeException(&quot;Second init in &quot; + clazz.getName());
            } else {
                calledInitializations.add(clazz);
            }
        }
    }

    private boolean isNotInSuperClassOf(Class&lt;? extends A&gt; clazz) {
        return getClass() == clazz;
    }
}

class B extends A {
    B() {
        checkForSecondInit(B.class);
    }
}

class C extends B {
    C() {
        checkForSecondInit(C.class);
    }
}

答案3

得分: 0

我们可以创建一个静态的Set集合,并将类名与包名一起添加到集合中。对于新对象,它会检查集合中是否存在已经存在的类名。

为了线程安全,集合被添加到同步块中。

import java.util.HashSet;
import java.util.Set;

public class A {
    static Set<String> set;
    public A(){
       if(set==null){
           synchronized(A.class){
              set = new HashSet<>();
           }
        }
        String classWithPackage = getClass().toString().split(" ")[1];
        if(set.contains(classWithPackage)){
            throw new RuntimeException("Only one instance can be created for: "+classWithPackage);
        }else {
            synchronized(A.class){
                set.add(classWithPackage);
            }
        }
    }
}
class B extends A{
    public B(){
        super();
    }

    public static void main(String[] args) {
        A a = new A();
        A a2 = new B();
        B b1 = new B();
    }
}
英文:

We can create static Set collection and add the classname along with package to set. For new object, it will check if there is a existing class name in the set or not.

For thread safety,set is added in synchronized block.

import java.util.HashSet;
import java.util.Set;

public class A {
    static Set&lt;String&gt; set;
    public A(){
       if(set==null){
           synchronized(set){
              set = new HashSet&lt;&gt;();
           }
        }
        String classWithPackage = getClass().toString().split(&quot; &quot;)[1];
        if(set.contains(classWithPackage)){
            throw new RuntimeException(&quot;Only one instance can be created for: &quot;+classWithPackage);
        }else {
            synchronized(set){
                set.add(classWithPackage);
            }
        }
    }
}
class B extends A{
    public B(){
        super();
    }

    public static void main(String[] args) {
        A a = new A();
        A a2 = new B();
        B b1 = new B();
    }
}

huangapple
  • 本文由 发表于 2020年8月10日 23:29:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63343239.html
匿名

发表评论

匿名网友

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

确定