Reputation: 44807
I have an abstract class and several concrete classes which extend it.
The abstract class has two constructors. I want one of the constructors to only be callable in one particular concrete class.
(I do know about the enum
pattern for Java state machines, but two levels of subclassing (and immutable POJOs) work better for the problem I'm solving.)
public abstract class SuperState {
public final long mValue;
protected SuperState(long value) { mValue = value; }
protected SuperState(SuperState last) { mValue = last.mValue + 1; }
...
}
public class FirstState extends SuperState {
public FirstState() { super(0); }
...
}
public class SecondState extends SuperState {
public SecondState(SuperState last) { super(last); }
...
}
public class ThirdState extends SuperState {
public ThirdState(SuperState last) { super(last); }
...
}
I want to make it a compile-time (or at least runtime) error for any subclass (apart from FirstState
) to call the SuperState(long value)
constructor.
Could I find out the type of the concrete class being constructed in the SuperState
constructor, and throw a runtime exception if it's not as expected?
Is there a was of having a "preferred" concrete class for an abstract class, such that it has some form of extra access?
Upvotes: 2
Views: 126
Reputation: 3230
I think you don't know clearly what you're doing.
You're saying that SuperState
has a constructor which is only callable from one specific implementation. Why? Is that subclass special? Why shouldn't other implementation call that?
If FirstState
is so special, maybe you want to have it as an internal class:
public abstract class SuperState {
public final long mValue;
private SuperState(long value) { mValue = value; }
protected SuperState(SuperState last) { mValue = last.mValue + 1; }
...
public static class FirstState {
//Can call SuperState(long) from here
}
}
If this doesn't seem appropriate to you, then probably you should leave both constructors open.
If, as it seems to me, you're making a chain-like structure, then you probably don't even want to have FirstState
as an accessible class:
public abstract class SuperState {
public final long mValue;
private SuperState(long value) { mValue = value; }
protected SuperState(SuperState last) { mValue = last.mValue + 1; }
...
private static class FirstState extends SuperState {
private FirstState() { super(0); }
}
public static SuperState getFirstState() { return new FirstState(); }
}
Upvotes: 3
Reputation: 1162
You can do it with default access modifier like this:
package a;
public abstract class SuperState {
public final long mValue;
SuperState(long value) { mValue = value; } // constructor has default access modifier
protected SuperState(SuperState last) { mValue = last.mValue + 1; }
...
}
package a;
public class FirstState extends SuperState {
public FirstState() { super(0); }
...
}
package b;
// is not able to access constructor SuperState(long) ie. calling contructor
// SuperState(long) will result in compile time error
public class SecondState extends SuperState {
public SecondState(SuperState last) { super(last); }
...
}
Upvotes: 2
Reputation: 9716
I think the only way to achieve it is to use analog of "friend". The trick is to have private Value class in the FirstState
which is possible to construct only by FirstState
. Other classes can see the FirstState.Value
class, but cannot instantiate it.
abstract class SuperState {
public final long mValue;
protected SuperState(FirstState.Value value) { mValue = value.value; }
protected SuperState(SuperState last) { mValue = last.mValue + 1; }
}
class FirstState extends SuperState {
public static class Value { private Value() {} }
private static Value value = new Value();
public FirstState() { super(value); }
}
class SecondState extends SuperState {
public SecondState(SuperState last) { super(last); }
}
Upvotes: 2
Reputation: 394016
One way I can think of, though I find it ugly :
protected SuperState(long value)
{
if (!this.getClass().getName().equals("SomeConcreteClassName"))
throw new SomeException ();
mValue = value;
}
Though Tom's comment about putting the FirstState class in the same package as SuperState and using package private access sounds better.
Upvotes: 2