Может ли переопределяющий метод иметь другой спецификатор доступа, чем в базовом классе?

какой модификатор доступа в абстрактном классе я должен использовать для метода, таким образом, подклассы могут решить, должен ли он быть публичным или нет? Можно ли "переопределить" модификатор в Java или нет?

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
    }
}

Я знаю, что будет проблема со статической привязкой, если кто-то зовет:

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

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

но, может быть, есть другой способ. Как я могу этого достичь?

6 ответов


можно ослабить ограничение, но не сделать его более строгим:

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
    }
}

создание оригинального метода private также не будет работать, так как такой метод не отображается в подклассах и поэтому не может быть переопределен.

Я бы рекомендовал использовать interfaces, чтобы выборочно выставить или скрыть метод:

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()'
}

... тогда работайте только с экземплярами через интерфейсы.


при переопределении методов можно изменить модификатор только на широкое один, а не наоборот. Например, этот код будет действителен:

public abstract class A {

    protected void method();
}

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

однако, если вы попытаетесь сузить видимость, вы получите ошибку времени компиляции:

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

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

для вашего случая я бы предложил сделать C не осуществляет A, as A'абстракция с подразумевает что есть не-частный method():

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

другой вариант-сделать method() реализации в C бросание исключения RuntimeException:

public class C extends A {

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

то, что вы просите, невозможно по очень веским причинам.

на принцип замещения Лисков в основном говорит: класс S является подклассом другого класса T только тогда, когда вы можете заменить любое вхождение какой-то "объект" с "с объекта" - не замечая.

Если вы позволите, чтобы s уменьшал публичный метод до частного, то вы больше не можете этого делать. Потому что внезапно этот метод, который можно было бы вызвать при использовании некоторых Т... больше не доступен для вызова на S.

короче говоря, наследование-это не то, что просто падает с неба. Это свойство классов, которые вы как программист несет ответственность за. Другими словами: наследование означает больше, чем просто запись "class S extends T" в исходном коде!


это невозможно из-за полиморфизма. Рассмотреть следующее. У вас есть метод в классе A С каким модификатором доступа, который не private. Почему бы и нет? Потому что если бы он был частным, то ни один другой класс не мог бы даже знать о его существовании. Поэтому это должно быть что-то другое, и это что-то еще должно быть доступно из где-то.

теперь предположим, что вы передаете экземпляр класса C to где-то. Но вы поднимите его до A заранее, и поэтому у вас есть этот код где-то:

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

один хороший пример того, как это сломалось составляет QSaveFile в Qt. В отличие от Java, C++ фактически позволяет снизить права доступа. Так они и сделали, запретив close() метод. То, что они закончили с QIODevice подкласс, который на самом деле не QIODevice больше. Если вы передадите указатель на QSaveFile к некоторому методу принятия QIODevice*, они все еще могут позвонить close() потому что это публично в QIODevice. Они "исправили" это QSaveFile::close() (личная) вызов abort(), поэтому, если вы сделаете что-то подобное, ваша программа немедленно аварийно завершит работу. Не очень хорошее "решение", но лучшего нет. И это просто пример плохого дизайна OO. Вот почему Java этого не позволяет.

редактировать

не то, чтобы я пропустил, что ваш класс абстрактен, но я также пропустил тот факт, что B extends C, а не A. Вот так, что ты ... хочу сделать это совершенно невозможно. Если метод public в B он также будет общедоступным во всех подклассах. Единственное, что вы можете сделать, это документ, который не должен называться и возможно, переопределить его, чтобы бросить UnsupportedOperationException. Но это приведет к тем же проблемам, что и с QSaveFile. Помните, что пользователи вашего класса могут даже не знать, что это экземпляр C таким образом, у них даже не будет возможности прочитать его документацию.

в целом это просто очень плохая идея О-мудро. Возможно, вам следует задать еще один вопрос о точной проблеме, которую вы пытаетесь решить с помощью этой иерархии, и, возможно, вы получите достойные советы о том, как это сделать правильно.


вот часть @Override контракт.

ответ : нет никакой возможности добиться того, что у вас есть.

уровень доступа не может быть более ограничительной, чем переопределяется уровень доступа метода. Например: если метод суперкласса объявленный public, то метод переопределения в подклассе не может быть private или protected.

это не проблема в отношении abstract только классы, но все классы и методы.


теория:

у вас есть определенный порядок модификаторов:

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

при переопределении метода можно увеличить, но не уменьшить уровень модификатора. Например,

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

практика:

в вашем случае, вы не можете включить ??? в некоторый модификатор, потому что private is низкий модификатор и private члены класса не наследуются.