fadedbee
fadedbee

Reputation: 44807

Java, how to stop inheriting classes from calling a particular abstract class constructor, apart from one of them?

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

Answers (4)

Giulio Franco
Giulio Franco

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

stjepano
stjepano

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

nogard
nogard

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

Eran
Eran

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

Related Questions