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

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

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

Ответ 1

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

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 не работает.

Это из-за полиморфизма:

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

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

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

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

Ответ 2

Метод переопределения CAN может вызывать любое исключение (время выполнения), независимо от того, переопределенный метод объявляет исключение

Пример:

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()");
    }
}

Ответ 3

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

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

Ответ 4

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

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 было разрешено выбросить, код клиента будет более правильным.

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

Ответ 5

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

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) бросать подкласс исключенного метода overriden

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 не проверяются исключениями. Только проверенные исключения должны появляться в бросках, если они не улавливаются.

Ответ 6

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

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

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

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

Открытый класс 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. Когда базовый класс и дочерний класс выбрасывают исключенное исключение

    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: Когда метод дочернего класса бросает исключение, ограниченное границами, по сравнению с тем же методом базового класса.

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

    }
}

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

Пример:

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

Ответ 10

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

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

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


Пример

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

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

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

Ответ 11

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

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

Класс Human расширяет класс 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() вызывается из объекта класса Mammal но во время выполнения JVM разрешит mammal.readAndGet() метода mammal.readAndGet() на вызов из класса Human потому что mammal удерживает new Human().

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

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

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

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

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

Ответ 12

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

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 выбрасывает исключение времени компиляции, когда метод печати генерирует исключение, метод print() базового класса не генерирует никаких исключений

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

Ответ 13

Метод переопределения подклассов может выставлять только несколько проверенных исключений, которые являются подклассами исключенного метода проверки суперкласса, но не могут выполнять множественные проверенные исключения, которые не связаны с исключенным методом суперкласса. Исключено исключение

Ответ 14

Java дает вам возможность ограничить исключения в родительском классе, поскольку он предполагает, что клиент будет ограничивать обнаруженное заражение. ИМХО вы не должны по существу никогда не использовать эту "функцию", потому что вашим клиентам может понадобиться гибкость по дороге.

Java - это старый язык, который плохо разработан. Современные языки не имеют таких ограничений. Самый простой способ избежать этого недостатка - сделать базовый класс throw Exception. Клиенты могут выделять более конкретные исключения, но делают ваши базовые классы очень широкими.

Ответ 15

Правило обработки проверок и непроверенных исключений в переопределенных методах

-Когда метод родительского класса не объявляет исключение, тогда дочерний класс overriding-   метод может объявить,

 1. No exception or
 2. Any number of unchecked exception
 3. but strictly no checked exception

-When метод родительского класса объявляет непроверенное исключение, тогда метод дочернего класса overriding- может объявить,

 1. No exception or
 2. Any number of unchecked exception 
 3. but strictly no checked exception

-Когда метод родительского класса объявляет проверенное исключение, метод дочернего класса overriding- может объявить,

 1. No exception or
 2. Same checked exception or
 3. Sub-type of checked exception or
 4. any number of unchecked exception

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

ссылка