Почему методы переопределения не могут исключать исключения, кроме переопределенного метода?

Я проходил через книгу SCJP 6 от Kathe sierra и натолкнулся на это объяснение исключения исключений в переопределенном методе. Я этого не понял. Может ли кто-нибудь объяснить это мне?

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

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

class A { public void foo() throws IOException {..} } class B extends A { @Override public void foo() throws SocketException {..} // allowed @Override public void foo() throws SQLException {..} // NOT allowed } 

SocketException extends IOException , но SQLException этого не делает.

Это из-за polymorphismа:

 A a = new B(); try { a.foo(); } catch (IOException ex) { // forced to catch this by the compiler } 

Если B решил бросить SQLException , компилятор не мог заставить вас его поймать, потому что вы ссылаетесь на экземпляр B своим суперclassом – A С другой стороны, любой подclass IOException будет обрабатываться предложениями (catch или throws), которые обрабатывают IOException

Правило, которое вы должны иметь, чтобы иметь возможность ссылаться на объекты своим суперclassом, – это Принцип замещения Лискова.

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

Переопределяющий метод МОЖЕТ выдавать любое исключение (время выполнения), независимо от того, объявляет ли переопределенный метод исключение

Пример:

 class Super { public void test() { System.out.println("Super.test()"); } } class Sub extends Super { @Override public void test() throws IndexOutOfBoundsException { // Method can throw any Unchecked Exception System.out.println("Sub.test()"); } } class Sub2 extends Sub { @Override public void test() throws ArrayIndexOutOfBoundsException { // Any Unchecked Exception System.out.println("Sub2.test()"); } } class Sub3 extends Sub2 { @Override public void test() { // Any Unchecked Exception or no exception System.out.println("Sub3.test()"); } } class Sub4 extends Sub2 { @Override public void test() throws AssertionError { // Unchecked Exception IS-A RuntimeException or IS-A Error System.out.println("Sub4.test()"); } } 

На мой взгляд, это провал в синтаксисе Java. Полиморфизм не должен ограничивать использование обработки исключений. На самом деле, другие компьютерные языки этого не делают (C #).

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

Чтобы проиллюстрировать это, рассмотрим:

 public interface FileOperation { void perform(File file) throws FileNotFoundException; } public class OpenOnly implements FileOperation { void perform(File file) throws FileNotFoundException { FileReader r = new FileReader(file); } } 

Предположим, что вы тогда пишете:

 public class OpenClose implements FileOperation { void perform(File file) throws FileNotFoundException { FileReader r = new FileReader(file); r.close(); } } 

Это даст вам ошибку компиляции, потому что r.close () генерирует исключение IOException, которое шире, чем FileNotFoundException.

Чтобы исправить это, напишите:

 public class OpenClose implements FileOperation { void perform(File file) throws IOException { FileReader r = new FileReader(file); r.close(); } } 

Вы получите другую ошибку компиляции, потому что вы выполняете операцию выполнения (…), но бросаете исключение, не включенное в определение метода интерфейса.

Почему это важно? Ну, потребитель интерфейса может иметь:

 FileOperation op = ...; try { op.perform(file); } catch (FileNotFoundException x) { log(...); } 

Если исключить IOException, код клиента будет более правильным.

Обратите внимание, что вы можете избежать такой проблемы, если используете исключенные исключения. (Я не предлагаю вам делать или нет, это философская проблема)

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

1) исключить одно и то же исключение

 public static class A { public void m1() throws IOException { System.out.println("A m1"); } } public static class B extends A { @Override public void m1() throws IOException { System.out.println("B m1"); } } 

2) бросать подclass исключенного метода исключения

 public static class A { public void m2() throws Exception { System.out.println("A m2"); } } public static class B extends A { @Override public void m2() throws IOException { System.out.println("B m2"); } } 

3) ничего не бросать.

 public static class A { public void m3() throws IOException { System.out.println("A m3"); } } public static class B extends A { @Override public void m3() //throws NOTHING { System.out.println("B m3"); } } 

4) Отсутствие RuntimeExceptions в бросках не требуется.

В бросках могут быть RuntimeExceptions или нет, компилятор не будет жаловаться на это. RuntimeExceptions не проверяются исключениями. Только проверенные исключения должны появляться в бросках, если они не зацепились.

скажем, у вас есть суперclass A с методом M1 throwin E1 и class B, полученный из A с методом M2, переопределяющим M1. M2 не может выбросить ничего РАЗЛИЧНОГО или МЕНЬШЕГО СПЕЦИАЛИЗИРОВАННОГО, чем E1.

Из-за polymorphismа клиент, использующий class A, должен иметь возможность рассматривать B, как если бы это было A. Inharitance ===> Is-a (B is-a A). Что делать, если этот код, относящийся к classу A, обрабатывал исключение E1, поскольку M1 объявляет, что он выбрал это проверенное исключение, но затем был выведен другой тип исключения? Если M1 выбрасывает IOException, M2 может отличить FileNotFoundException, поскольку это – исключение IOException. Клиенты A могут справиться с этим без проблем. Если бы исключение было шире, клиенты A не имели бы шанса узнать об этом и, следовательно, не имели бы шанса поймать его.

Возьмем интервью Вопрос. Существует метод, который генерирует исключение NullPointerException в суперclassе. Можем ли мы переопределить его методом, который вызывает исключение RuntimeException?

Чтобы ответить на этот вопрос, дайте нам знать, что такое исключение Unchecked и Checked.

  1. Проверенные исключения должны быть явно пойманы или размножены, как описано в разделе «Исходная обработка». У исключенных исключений это требование отсутствует. Их не нужно ловить или объявлять брошенными.

  2. Проверенные исключения в Java расширяют class java.lang.Exception. Исключенные исключения расширяют исключение java.lang.RuntimeException.

Открытый class NullPointerException расширяет RuntimeException

Исключенные исключения расширяют исключение java.lang.RuntimeException. Именно поэтому NullPointerException является исключением Uncheked.

Возьмем пример: Пример 1:

  public class Parent { public void name() throws NullPointerException { System.out.println(" this is parent"); } } public class Child extends Parent{ public void name() throws RuntimeException{ System.out.println(" child "); } public static void main(String[] args) { Parent parent = new Child(); parent.name();// output => child } } 

Программа будет успешно скомпилирована. Пример 2:

  public class Parent { public void name() throws RuntimeException { System.out.println(" this is parent"); } } public class Child extends Parent{ public void name() throws NullPointerException { System.out.println(" child "); } public static void main(String[] args) { Parent parent = new Child(); parent.name();// output => child } } 

Программа также будет успешно скомпилирована. Поэтому очевидно, что ничего не происходит в случае исключений Unchecked. Теперь давайте посмотрим, что произойдет в случае Проверенных исключений. Пример 3. Когда базовый class и дочерний class выбрасывают исключенное исключение

  public class Parent { public void name() throws IOException { System.out.println(" this is parent"); } } public class Child extends Parent{ public void name() throws IOException{ System.out.println(" child "); } public static void main(String[] args) { Parent parent = new Child(); try { parent.name();// output=> child }catch( Exception e) { System.out.println(e); } } } 

Программа будет успешно скомпилирована. Пример 4: Когда метод дочернего classа бросает исключение, ограниченное границами, по сравнению с тем же методом базового classа.

 import java.io.IOException; public class Parent { public void name() throws IOException { System.out.println(" this is parent"); } } public class Child extends Parent{ public void name() throws Exception{ // broader exception System.out.println(" child "); } public static void main(String[] args) { Parent parent = new Child(); try { parent.name();//output=> Compilation failure }catch( Exception e) { System.out.println(e); } } } 

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

Переопределяющий метод НЕ должен выдавать проверенные исключения, которые являются новыми или более широкими, чем те, которые объявлены переопределенным методом.

Пример:

 class Super { public void throwCheckedExceptionMethod() throws IOException { FileReader r = new FileReader(new File("aFile.txt")); r.close(); } } class Sub extends Super { @Override public void throwCheckedExceptionMethod() throws FileNotFoundException { // FileNotFoundException extends IOException FileReader r = new FileReader(new File("afile.txt")); try { // close() method throws IOException (that is unhandled) r.close(); } catch (IOException e) { } } } class Sub2 extends Sub { @Override public void throwCheckedExceptionMethod() { // Overriding method can throw no exception } } 

Переопределяющий метод НЕ должен выдавать проверенные исключения, которые являются новыми или более широкими, чем те, которые объявлены переопределенным методом.

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

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


пример

Скажем, у нас есть class A и его подclass B A имеет метод m1 а class B переопределяет этот метод (давайте назовем его m2 чтобы избежать путаницы ..). Теперь предположим, что m1 выбрасывает E1 , а m2 выбрасывает E2 , что является суперclassом E1 . Теперь напишем следующий fragment кода:

 A myAObj = new B(); myAObj.m1(); 

Обратите внимание, что m1 – это не что иное, как вызов m2 (опять же, сигнатуры методов одинаковы в перегруженных методах, поэтому не путайте с m1 и m2 .. они просто различаются в этом примере … они оба имеют одну и ту же подпись). Но во время компиляции весь java-компилятор работает с ссылочным типом (class A в этом случае) проверяет метод, если он присутствует, и ожидает, что программист обработает его. Так что, очевидно, вы бросите или поймаете E1 . Теперь, во время выполнения, если перегруженный метод выбрасывает E2 , который является суперclassом E1 , тогда … ну, это очень неправильно (по той же причине мы не можем сказать B myBObj = new A() ). Следовательно, Java этого не позволяет. Неконтролируемые исключения, вызванные перегруженным методом, должны быть одинаковыми, подclassами или несуществующими.

Чтобы понять это, рассмотрим пример, где у нас есть class Mammal который определяет метод readAndGet который читает некоторый файл, выполняет некоторую операцию над ним и возвращает экземпляр classа Mammal .

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

Класс Human расширяет class Mammal и переопределяет метод readAndGet чтобы вернуть экземпляр Human вместо экземпляра Mammal .

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

Для вызова readAndGet нам нужно обработать IOException потому что его проверенное исключение и readAndMethod млекопитающего бросают его.

 Mammal mammal = new Human(); try { Mammal obj = mammal.readAndGet(); } catch (IOException ex) {..} 

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

Метод readAndMethod из Mammal бросает IOException и потому что это проверенный компилятор исключений заставит нас поймать его всякий раз, когда мы вызываем readAndGet на mammal

Теперь предположим, что readAndGet в Human бросает любое другое исключенное исключение, например Exception, и мы знаем, что readAndGet будет вызван из экземпляра Human потому что у mammal есть new Human() .

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

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

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

Ну java.lang.Exception расширяет java.lang.Throwable. java.io.FileNotFoundException extends java.lang.Exception. Поэтому, если метод throws java.io.FileNotFoundException, то в методе переопределения вы не можете выбросить что-либо выше иерархии, чем FileNotFoundException, например, вы не можете выбросить java.lang.Exception. Однако можно было бы использовать подclass FileNotFoundException. Однако вы будете вынуждены обрабатывать FileNotFoundException в методе overriden. Сбейте код и попробуйте!

Эти правила существуют, поэтому вы не теряете оригинальное объявление бросков, расширяя спецификацию, так как polymorphism означает, что вы можете вызывать метод overriden над суперclassом.

Какое объяснение мы приписываем ниже

 class BaseClass { public void print() { System.out.println("In Parent Class , Print Method"); } public static void display() { System.out.println("In Parent Class, Display Method"); } } class DerivedClass extends BaseClass { public void print() throws Exception { System.out.println("In Derived Class, Print Method"); } public static void display() { System.out.println("In Derived Class, Display Method"); } } 

Класс DerivedClass.java выбрасывает исключение времени компиляции, когда метод печати генерирует метод Exception, print () базового classа не вызывает никакого исключения

Я могу объяснить это тем фактом, что Exception уже, чем RuntimeException, это может быть либо No Exception (Runtime error), RuntimeException и их дочерние исключения

  • Как анализировать XML с помощью анализатора SAX
  • Разбор JSON для JSON для JSON в JSONObject
  • Подписывает ли подпись метода в Java свой тип возврата?
  • Как вернуть массив из JNI в Java?
  • Почему XML используется для создания макетов пользовательского интерфейса в Android?
  • настраиваемый метод getView адаптера listview, который вызывается несколько раз и не имеет когерентного порядка
  • Какова наилучшая практика для округления поплавка до 2 десятичных знаков?
  • Передача данных с помощью Serializable
  • Чтобы использовать строковое значение в качестве имени переменной
  • Как скомпилировать пакеты в java?
  • Скрытые особенности Java
  • Interesting Posts

    Запустить Firefox и подождать, пока он не будет закрыт

    Autohotkey – Координаты движения мыши немного выключены

    Настроить мышь или клавиатуру для имитации левого клика и удержания или быстрого щелчка левой кнопкой мыши в Windows?

    Почему мне должно быть интересно, чтобы Java не обновляла дженерики?

    Замена кнопки Logitech Anywhere MX

    Как определить жесткий диск или USB-диск данного диска с помощью cmd?

    Сценарий Bash & 'su', дающий ошибку «стандарт должен быть tty»,

    В чем разница между строкой и строкой в ​​C #?

    Имеет ли Haskell переменные?

    Нужно ли нам по-прежнему использовать косые черты в HTML5?

    C ++ standard: разыменование NULL-указателя для получения ссылки?

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

    Самый простой способ сделать огонь и забыть метод в C #?

    Как добавить пользователей на FTP-сайт в IIS Windows 7?

    Как изменить каталог установки программы по умолчанию в Windows 7?

    Давайте будем гением компьютера.