I was asked this question in an interview.
There are 3 classes A
, B extends A
& C extends B
. We have to design these classes conforming to these constraints
A
, one instance of B
& one instance of C
using their default constructor with new
keyword.I suggested an approach using an static Map<Class, Object>
. So for e.g. when somebody called new B()
it would check if map.contains(B.class)
. If yes then throw exception & if not then save instance in map & let the object be created.
But the next question was how would I enforce this approach on each class? As per my approach each constructor would have to carefully populate the map otherwise it will break the constraint.
How would I solve this problem?
Instantiating a Class When you create an object, you are creating an instance of a class, therefore "instantiating" a class. The new operator requires a single, postfix argument: a call to a constructor. The name of the constructor provides the name of the class to instantiate.
The result of this is that you have two reference variables that point to the same object. You can use the same reference variable to create more than one instance of a class.
To create a new instance of an object, we use the "new" keyword. This keyword creates a new instance of an object, which we can then assign to a variable, or invoke methods.
But the next question was how would I enforce this approach on each class? As per my approach each constructor would have to carefully populate the map otherwise it will break the constraint.
Simply by putting that map, and the code around it into some distinct Utility class. So that each and of your classes could do something like:
public WhateverClassConstructor() {
synchroized(someLock) {
SingeltonChecker.ensureUnique(this);
with
public static void ensureUnique(Object objectToAdd) {
Class<?> classToAdd = o.getClass();
...
And given the fact that C extends B extends A, you probably only need that call in the constructor of class A. On the other hand, imagine that your first call to new C()
causes an exception with the C-constructor, but a second call would not. Sure, that is a problem in itself, but still, the question remains: how do you ensure an object was fully and correctly initialized before adding it to such a map?!
Thus: there is a ton of things to consider when writing such utility code. Thus my answer would focus on the impractically, almost stupid-ness of the given design point, and suggest to throw it away and go for other solutions, for example something as simple as:
public enum SingletonObjectHolder {
private final Object hold;
A_INSTANCE(new A()), B_INSTANCE(new B())...
public Object getSingleton() { return hold; }
private SingletonObjectHolder(Object o) { hold = o };
Don't waste too much time trying to give people a technical answer, when the real point is to not shoot yourself into the foot. And make no mistake: getting that map-based "singleton" approach to work robust and correct, for all kinds of contexts, consider that really hard.
In other words: if I would ask you this question in an interview, I would want to hear an answer that challenges that horrible design point. Sure, spent 10% of your time outlining a solution for the given scenario. But spend 90% of the time explaining why it is so bad, and why other solutions would be much better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With