masterdany88
masterdany88

Reputation: 5351

How to protect a method implementing an interface from overriding?

I have interface:

public interface Doable {
    void doSomething();
}

and the class that implements it:

public class DoJump() implements Doable {
    @Override
    private void doSomething() {
        fireJumpHandler();
    }
}

This is stupid example, but I would like to present the problem.

This code doesn't compile, I am getting an error in Eclipse IDE:

Cannot reduce the visibility of the inherited method from Doable

I have common interface that declares a method. This method is overriden in concrete class. I would like to avoid another class that can extend this class (DoJump), so I would like to hide this method from sub classes. I would like to use private modifier, but Java does not allow me to do it.

Why it is impossible, and how to workaround it?

Upvotes: 2

Views: 156

Answers (2)

Spotted
Spotted

Reputation: 4091

Add final to DoJump declaration to prevent this class to be overriden (and therefore doSomething() to be overriden too).

public final class DoJump implements Doable {
    @Override
    public void doSomething() {
        fireJumpHandler();
    }
}

If you still need to be able to inherit DoJump but you don't want doSomething() to be overriden, put the final modifier in the method signature

public class DoJump implements Doable {
    @Override
    public final void doSomething() {
        fireJumpHandler();
    }
}

Upvotes: 2

Zabuzard
Zabuzard

Reputation: 25943

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.

Upvotes: 3

Related Questions