Наследование, сигнатура метода, переопределение метода и предложение throws - PullRequest
15 голосов
/ 27 января 2012

Мой Parent класс:

import java.io.IOException;
public class Parent {
        int x = 0;
        public int getX() throws IOException{
        if(x<=0){
         throw new IOException();
        }
       return x;
      }
 }

I extend этот класс для записи подкласса Child:

public class Child1 extends Parent{
     public int getX(){
        return x+10;
   }
}

Обратите внимание, что при переопределении метода getX в классе Child я удалил предложение throws из определения метода. Теперь это приводит к аномальному поведению ожидаемый компилятор:

new Parent().getX() ;

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

new Child().getX() ;

компилируется, не заключая его в try-catch блок.

Но приведенные ниже строки кода требуют блока try-catch.

Parent p = new Child();
p.getX();

Так как это можно предвидеть, например, используя ссылку на родительский класс для вызова дочернего метода во время полиморфизма во время выполнения, почему разработчики Java не сделали обязательным включение предложения throws в определение метода при переопределении определенного родителя метод класса? Я имею в виду, если у метода родительского класса есть предложение throws в своем определении, то при переопределении метод overriding должен также включать предложение throws, не так ли?

Ответы [ 4 ]

32 голосов
/ 27 января 2012

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

Использование переопределенного метода через ссылку типа Parent никогда не приведет к нарушению договора "он может выдать IOException" - отсутствие исключения не нарушает договор. И наоборот (если родительский элемент не объявляет исключение, но переопределяющий метод делает) будет нарушением контракта.

3 голосов
/ 27 января 2012

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

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

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

2 голосов
/ 07 июля 2015

Легко запомнить

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

Этот код действителен

public class A  {

    protected String foo() throws Exception{
        return "a";
    }

    class B extends A {
        @Override
        public String foo() throws IOException{
            return "b";
        }
    }
}

Переопределенный метод foo имеет открытый доступ, не защищен и выдает IOException, который является потомкомИсключение

Этот код недействителен

public class A  {

    public String foo() throws IOException{
        return "a";
    }

    class B extends A {
        @Override
        protected String foo() throws Exception{
            return "b";
        }
    }
}

Метод переопределенного foo имеет более ограниченный модификатор доступа и выдает исключение, которое НЕ является потомком IOException

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

Этот код действителен

public class A  {

    public String foo() throws IOException{
        return "a";
    }

    class B extends A {
        @Override
        public String foo(){
            return "b";
        }
    }
}
2 голосов
/ 28 января 2012

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

Но помните одну вещь: если вы хотите включить предложение throws в метод подкласса, то предложение throws должно ассоциироваться с исключением, которое должно быть таким же, или с подклассом исключения, которое выдается его методом суперкласса. например -

class Super{
    void a()throws IOException{
        ......
    }
}
class Sub extends Super{
    void a()throws IOException{
        ......
    }
}

Метод a () класса Sub должен генерировать IOException или любой подкласс IOException, в противном случае компилятор выдаст ошибку.

Это означает, что если вы напишите

void a()throws Exception

в классе Sub, тогда это приведет к ошибке компиляции.

...