Почему Java допускает только один метод X при реализации 2 интерфейсов, которые оба объявляют X, но отличаются в своих предложениях throws? - PullRequest
3 голосов
/ 12 апреля 2011

Это тесно связано, но не совпадает с вопросом: Java - конфликт имен методов при реализации интерфейса

Когда есть два интерфейса Java, которые совместно используют метод с почти одна и та же сигнатура, например, так:

import java.io.IOException;

interface ISomething1 {
    void doSomething();
}

interface ISomething2 {
    void doSomething() throws IOException;
}

class Impl implements ISomething1, ISomething2 {
  public void doSomething() {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

  public static void main(String args[]) throws IOException {
    Impl i = new Impl();
    i.doSomething();
  }
}

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

Обратите также внимание, что в приведенном выше примере реализация Impl.doSomething не должна генерировать IOException, так как в противном случае компилятор жалуется, что ISomething1.doSomething также должен объявить, что он генерирует IOException, эффективно делая предложение throwsISomething2.doSomething бесполезно.

Я что-то упускаю здесь очевидное?Заранее спасибо за ответы.

Ответы [ 5 ]

2 голосов
/ 12 апреля 2011

Вы можете ожидать, что компилятор узнает, собирается ли ваш клиентский код вызывать void doSomething(); или void doSomething() throws IOException; в зависимости от того, обрабатывает ли этот код клиента исключение.

Но компилятор Java не настолько умен.

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

Единственное, что составляет тип сигнатуры метода, - это имя и параметры.

2 голосов
/ 12 апреля 2011

При реализации метода в дочернем классе вы можете скрыть или выбросить исключения, которые «расширяют» исключение из базового класса.

В вашем случае:

public void doSomething() {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

IOException не является исключением времени выполнения и должно быть объявлено в сигнатуре метода. Но это:

public void doSomething() throws IOException {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

не подчиняется описанным правилам и недействительно из-за

interface ISomething1 {
    void doSomething();
}

P.S. Это не разные подписи. Если вы хотите создать новое исключение и расширить оба интерфейса - вы можете инкапсулировать IOException следующим образом:

throw new RuntimeException(new IOException());

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

1 голос
/ 12 апреля 2011

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

0 голосов
/ 12 апреля 2011

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

Но чтобы ответить на ваш вопрос ... Допустим, есть некоторый код, который хочет объект типа ISomething1, потому что ему нужно что-то делать с ним. Если ваш класс Impl фактически выдал исключение, вызывающий код был бы совершенно не готов обработать исключение.

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

0 голосов
/ 12 апреля 2011

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

Один намек на вашу путаницу состоит в том, что у вас есть два интерфейса, но вы их нигде не используете. то есть вы можете удалить их, и ваша main () скомпилируется просто отлично.

...