2

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?

fadedbee
  • 42,671
  • 44
  • 178
  • 308
  • 1
    I don't think that this is possible on a "normal" way. But you can move `FirstState` into the same package as `SuperState` and make `protected SuperState(long)` *package-private*. But this won't prevent the possibility of moving `SecondState` as well. – Tom Feb 06 '15 at 11:02

4 Answers4

3

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(); }
}
Giulio Franco
  • 3,170
  • 15
  • 18
  • Best answer so far. Yes, it's a bit chain like - the constructors for a state (at each level of the hierarchy) may need top copy-in (and modify) data from the last state. There is no chain after construction. The more similar two states are (the more superclasses in common) the more data is propagated. – fadedbee Feb 06 '15 at 11:16
  • Not quite what I was looking for, but the best of the available options, as Java has no `friend`. – fadedbee Feb 06 '15 at 11:40
  • If it's a friend use case, then you should use a friend-simulating pattern: http://stackoverflow.com/questions/182278/is-there-a-way-to-simulate-the-c-friend-concept-in-java . The suggestion, which I put at the beginning of my answer, is that you should make it clearer, at least to yourself, which is the abstract concept you need, and then determine an implementation based on that. – Giulio Franco Feb 06 '15 at 13:56
2

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.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • @Bret Well, OP asked for either compile time or run time error. I agree that compile time error would be better. – Eran Feb 06 '15 at 11:07
2

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); }
}
nogard
  • 9,432
  • 6
  • 33
  • 53
2

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); }
    ...
}
stjepano
  • 1,052
  • 7
  • 15