Вопрос-ответ

Why can't overriding methods throw exceptions broader than the overridden method?

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

Я просматривал книгу Кэти Сьерра по SCJP 6 и наткнулся на это объяснение генерирования исключений в переопределенном методе. Я совершенно не понял. Кто-нибудь может мне это объяснить?


Переопределяющий метод НЕ должен генерировать проверенные исключения, которые являются новыми или более широкими, чем те, которые объявлены переопределяемым методом. Например, метод, который объявляет 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

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

Пример:

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. Полиморфизм не должен ограничивать использование обработки исключений. Фактически, другие компьютерные языки этого не делают (C #).

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

Ответ 4

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

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

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 в throws не требуется.

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

java