Почему метод переопределения класса класса не должен быть более строгим, чем базовый класс в java.Why компилятор выдает ошибку? можете ли вы, пожалуйста, объяснить причину этого?
Почему метод переопределения класса не должен быть более строгим, чем базовый класс в java?
Ответ 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 был закрытым, это бы выдало ошибку, потому что мы находимся в другом классе, который поэтому не может получить доступ к методу.