1

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

akshay
  • 33
  • 5

3 Answers3

2

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<> instances in the top parent and check for duplicity in the constructor. This is not a good conventional solution. The singelton pattern is usual.

public class TopClass {
    private static final Set<Class<? extends TopClass>> instances = new HashSet<>();

    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.

Hawk
  • 2,042
  • 8
  • 16
  • Could you please explain why you consider your 2nd solution as "not good"? Because it's exactly what the OP is asking for – fps Aug 10 '20 at 15:56
  • @fps well because singleton pattern is the widely known and accepted solution to this problem AFAIK. So why invent something new, if anyone would know what `getInstance()` does on first glance? So basically the reason is convention, I guess. – Hawk Aug 10 '20 at 16:02
  • Please take a look at my edited question. It is throwing exception while creating an instance of B as it was calling no arg constructor of its parent class and finding that its static variable is not null. As I said it should let call constructor of B once. – akshay Aug 10 '20 at 16:02
  • @Hawk, please take a look at the code I posted in the question. As your answer does not say anything about inheritance hierarchy whereas I had explicitly added in the question. – akshay Aug 10 '20 at 16:14
  • @Hawk, I want to create an object using constructor only. Not using the getInstance method. – akshay Aug 10 '20 at 16:30
  • 1
    Without a factory method you can wrap the constructor in a conditional (as in class A) or use the 2nd method with collecting all Class<> instances. That is the most concise and extensible solution (i.e. it works for any amount of subclasses without _any_ code in the subclass) – Hawk Aug 10 '20 at 16:42
0

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<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);
    }
}
d3nnisp
  • 21
  • 4
0

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<String> set;
    public A(){
       if(set==null){
           synchronized(set){
              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(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();
    }
}
KESHAV KUMAR
  • 140
  • 1
  • 7