I'd like to answer your last question "How to workaround it?"
as this is not described in the related question. Create a second interface NotDoable
which simply does not have doSomething()
declared. Then let your DoJump
implement both interfaces. Give everyone that shouldn't override doSomething
a reference to the interface NotDoable
instead of the true type DoJump
. Then they won't know that the object truly can doSomething
, they won't know per class design. Of course, one can workaround this but one actually can workaround everything. The class design is more correct this way. Here's some code:
public interface Doable {
public void doSomething();
}
public interface NotDoable {
}
public class DoJump implements Doable, NotDoable {
@Override
public void doSomething() {
System.out.println("hi");
}
public NotDoable meAsNotDoable() {
return this;
}
public static void main(String[] args) {
DoJump object = new DoJump();
// This call is possible, no errors
object.doSomething();
NotDoable hidden = object.meAsNotDoable();
// Not possible, compile error, the true type is hidden!
hidden.doSomething();
}
}
But as said, one can workaround this by using if (hidden instanceof DoJump) { DoJump trueObject = (DoJump) hidden; }
. But well, one can also access private values via reflection.
Other classes now implement NotDoable
instead of extending DoJump
. If you declare everything others should know about DoJump
in this interface, then they only can do what they should do. You may call this interface IDoJump
and the implementing class DoJump
, a common pattern.
Now the same a bit more concrete.
public interface IDog {
public void bark();
}
public interface ICanFly {
public void fly();
}
public class FlyingDog implements IDog, ICanFly {
@Override
public void bark() {
System.out.println("wuff");
}
@Override
public void fly() {
System.out.println("Whuiiii");
}
public static void main(String[] args) {
FlyingDog flyingDog = new FlyingDog();
// Both works
flyingDog.fly();
flyingDog.bark();
IDog dog = (IDog) flyingDog;
// Same object but does not work, compile error
dog.fly();
ICanFly canFly = (ICanFly) flyingDog;
// Same object but does not work, compile error
canFly.bark();
}
}
And now an extending class.
public class LoudDog implements IDog {
@Override
public void bark() {
System.out.println("WUUUUFF");
}
// Does not work, compile error as IDog does not declare this method
@Override
public void fly() {
System.out.println("I wanna fly :(");
}
}
In the end, be aware that if others know that their IDog
actually is a FlyingDog
(and they cast it), then they must be able to call fly()
as a FlyingDog
must can fly. Furthermore, they must be able to override the behavior as long as they follow the specification of fly()
given by its method-signature
. Imagine a subclass
called PoorFlyingDog
, he needs to override the default behavior, else he can perfectly fly, but he is a poor flyer.
Summarized: Hide to others that you're actually a DoJump
, also hide that you are a Doable
, pretend to only be a NotDoable
. Or with the animals, pretend to only be an IDog
instead of a FlyingDog
or ICanFly
. If the others don't cheat (casting), they won't be able to use fly()
on you, though you actually can fly.