ECMAScript
ECMAScript

Reputation: 4649

Making methods only accessible to child classes, not instantiated objects

public class Flight{
  private int flying = 0;   
  public boolean fly() {
     flying = 1;
     return isFlying();
  }
  private isFlying(){
    return flying > 0;
  }
}

public class CargoFlight extends Flight{

  public boolean startFlight(int passengers)
    if (passengers <= 0){
      return false;
    }

    return fly(); // Want to be able to do this

  }
}

 public class Airport{
   public static void main(){
     CargoFlight f1 = new CargoFlight();
     f1.fly(); // Don't want to be able to do this
  }
}

f1 has the property fly(), is there any way to restrict it such that the method fly() can be called inside the body of the classes extending Flight (like CargoFlight here), but cannot be called using the instances of the subclasses (like f1)? I have added comments to the code to make it clear.

Upvotes: 4

Views: 3639

Answers (1)

Boann
Boann

Reputation: 50061

The nearest access specifier to what you want is protected. However, protected members are still always accessible to other classes in the same package, so it won't prevent access from your Airport class.

If you really need the subclass to block access to the method except to the subclass, then you can override it in the subclass to always throw an exception, then use super to invoke the original method:

public class Flight {
    private int flying = 0;

    protected boolean fly() {
        flying = 1;
        return isFlying();
    }

    private boolean isFlying() {
        return flying > 0;
    }
}

public class CargoFlight extends Flight {
    @Override
    protected boolean fly() {
        throw new IllegalAccessError();
    }

    public boolean startFlight(int passengers) {
        if (passengers <= 0) {
            throw new IllegalArgumentException();
        }

        return super.fly();
    }
}

The flaw with any solution though, is that it violates the Liskov substitution principle. A CargoFlight is no longer a proper instance of Flight because it doesn't have the normal fly method that other Flights have. If you intend fly only to be called by subclasses and never directly, then it's okay (although you should document that rule in the method Javadoc), but it still leaves you without the nicety of having a polymorphic method to call to tell generic Flights to fly.

A nicer solution, if it can fit with your design, would be to have fly and startFlight be the same method (that means same name and same arguments, and the same return type or a subtype), so then the subclass method could simply override the base implementation. The only method outside callers would see is fly. That means that your passengers argument either needs to be part of the base method Flight.fly too, or, remove it from both method implementations and make it into a separate property setPassengers for those subclasses that need it:

public class CargoFlight extends Flight {
    private int passengers = 0;

    public void setPassengers(int p) {
        passengers = p;
    }

    @Override
    public boolean fly() {
        if (passengers <= 0) {
            throw new IllegalStateException(); // or whichever
        }

        return super.fly();
    }
}

Upvotes: 6

Related Questions