Почему метод переопределения класса не должен быть более строгим, чем базовый класс в java? - программирование
Подтвердить что ты не робот

Почему метод переопределения класса не должен быть более строгим, чем базовый класс в java?

Почему метод переопределения класса класса не должен быть более строгим, чем базовый класс в java.Why компилятор выдает ошибку? можете ли вы, пожалуйста, объяснить причину этого?

4b9b3361

Ответ 1

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

public class Super
{
    public void print()
    {
        System.out.println("Hello!");
    }
}

public class Sub extends Super
{
    @Override
    void print() // Invalid
    {
        System.out.println("Package access");
    }
}

Теперь из другого пакета предположим, что у нас было:

public void printSuper(Super x)
{
    x.print();
}

и мы назвали это следующим образом:

printSuper(new Sub());

Что вы ожидаете от этого? Вы переопределяете метод, поэтому он должен печатать "доступ к пакету", но тогда это означает, что вы вызываете метод доступа к пакетам из другого пакета...

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

Ответ 2

Вы не можете сделать модификатор доступа более ограничительным, поскольку это нарушит основное правило наследования, что экземпляр подкласса должен быть заменен вместо экземпляра суперкласса.

Например, Предположим, что класс Person имеет открытый метод getName, который используется многими классами (включая не-под-классы). Но кто-то просто добавил Employee как подкласс Person и getName в Сотрудник защищен, к которому должны обращаться только подклассы, тогда предыдущий код начнет ломаться, а Employee не будет заменен объекту Person.

Следовательно, java решила навязать это ограничение.

Ответ 3

Рассмотрим пример ниже

class Mammal {
    public Mammal readAndGet() throws IOException {//read file and return Mammal object}
}

class Human extends Mammal {
    @Override
    public Human readAndGet() throws FileNotFoundException {//read file and return Human object}
}

И если мы выполним ниже код

Mammal mammal = new Human();
Mammal obj = mammal.readAndGet();

И мы знаем, что для компилятора mammal.readAndGet() вызывается из объекта класса Mammal но во время выполнения JVM разрешит mammal.readAndGet() метода mammal.readAndGet() на вызов из класса Human потому что mammal удерживает new Human().

Метод readAndGet определен как общедоступный в Mammal и для того, чтобы сделать его менее ограничительным в классе Human нам нужно либо удалить спецификатор доступа (по default), либо сделать его protected или private.

Теперь предположим

  • Если мы определяем readAndGet как default или protected в Human но Human определяется в другом пакете
  • Если мы определим readAndGet как личное в Human

Мы знаем, что JVM разрешит readAndGet метода readAndGet во время выполнения, но компилятор не знает об этом, и в обоих случаях код успешно скомпилируется, поскольку для компилятора readAndGet вызывается из класса Mammal.

Но в обоих случаях во время выполнения JVM не сможет получить доступ к readAndGet от Human, поскольку он будет ограничен из-за ограниченного доступа readAndGet в классе Human.

А также мы не можем быть уверены в том, кто будет расширять наш класс в будущем и в каком пакете он будет это делать, и если этот парень сделает переопределенный метод менее строгим, JVM не сможет вызвать метод, и код будет прерываться во время выполнения,

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

Существуют и другие правила, которым мы должны следовать при переопределении методов, и вы можете прочитать больше о том, почему мы должны следовать правилам переопределения методов, чтобы узнать причины.

Ответ 4

Переопределяющее сообщение не должно быть более ограничительным, чем переопределенный метод, потому что экземпляр дочернего класса всегда должен быть в состоянии использовать в месте, где ожидается экземпляр суперкласса. Но если метод более ограничен, он может быть недоступен, если вызов поступает из другого класса или пакета. Например, представьте это:

//File Number 1
public class Human {
    public int age = 21;
    public int getAge() {
        System.out.println("getAge() method in Human");
        return age;
    }
}

//File Number 2
public class Teacher extends Human {
    public int getAge() {
        System.out.println("getAge() method in Teacher");
        return age;
    }
}

//File Number 3
public class Characteristics {  
    public static void showAgeOfObject(Human human) {
        human.getAge();
    }

    public static void main(String[] args) {
        Human human = new Human();
        Teacher teacher = new Teacher();

        showAgeOfObject(human);     
        showAgeOfObject(teacher);
    }
}

Теперь, если оба метода getAge() являются публичными, это будет отображаться

getAge() method in Human
getAge() method in Teacher

Но если бы getAge() в Teacher был закрытым, это бы выдало ошибку, потому что мы находимся в другом классе, который поэтому не может получить доступ к методу.