Can an overriding method have a different access specifier from that in the base class?

烈酒焚心 提交于 2019-12-23 06:48:38

问题


Which access modifier, in an abstract class, do I have to use for a method, so the subclasses can decide whether it should be public or not? Is it possible to "override" a modifier in Java or not?

public abstract class A {

    ??? void method();
}

public class B extends A {
    @Override
    public void method(){
        // TODO
    }
}

public class C extends B {
    @Override
    private void method(){
        // TODO
    }
}

I know that there will be a problem with static binding, if someone calls:

// Will work
A foo = new B()
foo.method();

// Compiler ?
A foo = new C();
foo.method();

But maybe there is another way. How I can achieve that?


回答1:


It is possible to relax the restriction, but not to make it more restrictive:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    public void method(){    // OK
    }
}

public class C extends A {
    @Override
    private void method(){    // not allowed
    }
}

Making the original method private won't work either, since such method isn't visible in subclasses and therefore cannot be overriden.

I would recommend using interfaces to selectively expose or hide the method:

public interface WithMethod {
    // other methods
    void method();
}

public interface WithoutMethod {
    // other methods
    // no 'method()'
}

public abstract class A {
    protected void method();
}

public class B extends A implements WithMethod {
    @Override
    public void method(){
      //TODO
    }
}

public class C extends B implements WithoutMethod {
    // no 'method()'
}

... then only work with the instances through the interfaces.




回答2:


When overriding methods, you can only change the modifier to a wider one, not vice versa. For example this code would be valid:

public abstract class A {

    protected void method();
}

public class B extends A {
    @Override
    public void method() { }
}

However, if you try to narrow down the visibility, you'd get a compile-time error:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    private void method() {}
}

For your case, I'd suggest to make C not implementing A, as A's abstraction implies that there's a non-private method():

public class C {
    private void method(){
      //TODO
    }
}

Another option is to make the method() implementation in C throwing a RuntimeException:

public class C extends A {

    @Override
    public void method(){
        throw new UnsupportedOperationException("C doesn't support callbacks to method()");
    }
}



回答3:


What you are asking for is not possible for very good reasons.

The Liskov substitution principle basically says: a class S is a subclass of another class T only then, when you can replace any occurrence of some "T object" with some "S object" - without noticing.

If you would allow that S is reducing a public method to private, then you can't do that any more. Because all of a sudden, that method that could be called while using some T ... isn't available any more to be called on S.

Long story short: inheritance is not something that simply falls out of the sky. It is a property of classes that you as the programmer are responsible for. In other words: inheritance means more than just writing down "class S extends T" in your source code!




回答4:


This is impossible because of the polymorphism. Consider the following. You have the method in class A with some access modifier which is not private. Why not private? Because if it was private, then no other class could even know of its existence. So it has to be something else, and that something else must be accessible from somewhere.

Now let's suppose that you pass an instance of class C to somewhere. But you upcast it to A beforehand, and so you end up having this code somewhere:

void somewhereMethod(A instance) {
    instance.method(); // Ouch! Calling a private method on class C.
}

One nice example how this got broken is QSaveFile in Qt. Unlike Java, C++ actually allows to lower access privileges. So they did just that, forbidding the close() method. What they ended up with is a QIODevice subclass that is not really a QIODevice any more. If you pass a pointer to QSaveFile to some method accepting QIODevice*, they can still call close() because it's public in QIODevice. They “fixed” this by making QSaveFile::close() (which is private) call abort(), so if you do something like that, your program immediately crashes. Not a very nice “solution”, but there is no better one. And it's just an example of bad OO design. That's why Java doesn't allow it.

Edit

Not that I missed that your class is abstract, but I also missed the fact that B extends C, not A. This way what you want to do is completely impossible. If the method is public in B, it will be public in all subclasses too. The only thing you can do is document that it shouldn't be called and maybe override it to throw UnsupportedOperationException. But that would lead to the same problems as with QSaveFile. Remember that users of your class may not even know that it's an instance of C so they won't even have a chance to read its documentation.

Overall it's just a very bad idea OO-wise. Perhaps you should ask another question about the exact problem you're trying to solve with this hierarchy, and maybe you'll get some decent advises on how to do it properly.




回答5:


Here is a part of the @Override contract.

The answer is : there isn't any possibility to achieve what you have.

The access level cannot be more restrictive than the overridden method's access level. For example: if the superclass method is declared public then the overridding method in the sub class cannot be either private or protected.

This is not a problem concerning abstract classes only but all classes and methods.




回答6:


THEORY:

You have the determined modifiers order:

public <- protected <- default-access X<- private

When you override the method, you can increase, but not decrease the modifier level. For example,

public -> []
protected -> [public]
default-access -> [public, default-access]
private -> []

PRACTICE:

In your case, you cannot turn ??? into some modifier, because private is the lowest modifier and private class members are not inherited.



来源:https://stackoverflow.com/questions/35956217/can-an-overriding-method-have-a-different-access-specifier-from-that-in-the-base

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!