Есть ли ключевое слово "noreturn" в Java - PullRequest
3 голосов
/ 08 апреля 2011

Иногда я хочу написать функцию error (), которая обязательно вызовет System.exit () в конце, означая, что эта функция никогда не вернется. Однако, если я вызываю error () в других функциях, я хочу написать так:

int fun() {
...
  error();
}

Но компилятор настаивает на возвращении значения int после вызова error (), поскольку он не знает, что error () никогда не вернется. Я, конечно, могу вернуть произвольный тип int, но если возвращаемый тип является сложным классом, мне нужно создать его в коде, который является пустой тратой времени. Есть ли способ сообщить компилятору, что функция никогда не вернется?

Ответы [ 7 ]

5 голосов
/ 08 апреля 2011

В сигнатуре метода Java нет способа сообщить компилятору о невозможности возврата метода. Без этого у компилятора нет другого выбора, кроме как предположить, что он может вернуться. (Действительно, правила JLS о достижимости / определенном назначении прямо заявят об этом ... если вы захотите пройтись по ним.)

Я колеблюсь между двумя подходами:

int fun() {
    ...
    error();
    return 0;  // NOT REACHED
}

и

int fun() {
    ...
    error();
    throw new AssertionError("not reached");
}

Ни то, ни другое не вполне удовлетворительно.

В первой версии комментарий важен, так как кто-то, читающий ваш код в первый раз, может не осознавать, что error никогда не вернется.

Вторая версия является более надежной, поскольку она даст вам «быстрый сбой», если кто-то изменит поведение метода error, чтобы он действительно вернулся. (Первая версия приведет к тому, что метод fun вернет поддельное значение ... что может привести к другим непредвиденным последствиям.)


В связанной точке метод, который отключает JVM путем вызова System.exit(), может быть проблематичным. Лучшая идея состоит в том, чтобы выбросить непроверенное исключение «конец света» и перехватить его на самом внешнем уровне вашего основного потока. Для исключений, создаваемых в других потоках, одной из идей будет установка обработчика необработанных исключений по умолчанию, который использует Thread.interrupt() для уведомления основного потока о том, что было сгенерировано исключение "конец света".

Но дело в том, что System.exit() вызовы, сделанные глубоко в коде, проблематичны; например если вы перепрофилируете свой код для запуска в более крупной среде; например в веб-контейнере.

5 голосов
/ 08 апреля 2011

Вы должны выбросить RuntimeException .

3 голосов
/ 07 апреля 2015

Java не позволяет вам объявить, что ваш метод никогда не возвращается, но компилятор знает оператор , который никогда не возвращает: throw.

Вы можете использовать это в своей ситуации, объявив свой метод «без возврата», чтобы возвратить RuntimeException, и вызвать его, используя throw error(). Таким образом вы переносите проблему «метода, который не возвращает», на «оператор, который не возвращает».

Поскольку ваш метод error() никогда не возвращается, вам на самом деле не нужно возвращать объявленный RuntimeException, а throw в throw error() никогда не выполняется.

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

Пример: * * один тысяча двадцать-одна

private RuntimeException error() {
    throw new EndOfTheWorldException();
    // Java knows that code after throw will not be reached
    // so no return is required here.  Or, if you insist, you
    // could call System.exit() and return null afterwards.
}

int fun() {
    // ...
    throw error();
    // Again, no need for a return here, the compiler understands
    // that the statement above won't return.
}

В качестве альтернативы, а может и более явно, вы можете изменить этот код, чтобы сделать error() фабрикой для создаваемого исключения:

private RuntimeException makeError() {
    return new EndOfTheWorldException();
}

int fun() {
    throw makeError();
}
1 голос
/ 08 апреля 2011

Не уверен, почему ты хочешь это сделать. Это был бы более чистый код, более тестируемый и обслуживаемый, если бы вы бросили исключение; Вы можете использовать Thread.setDefaultUncaughtExceptionHandler() для выхода.

Но если вы настаиваете:

public class HaltAndCatchFireError extends Error {
   public HaltAndCatchFireError() {
      System.exit(1);
   }
}

А потом, куда вы хотите выйти:

int fun() {
   ...
   throw new HaltAndCatchFireError();
}
1 голос
/ 08 апреля 2011

вернуть 0 или, в сложных случаях, вернуть ноль.

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

Вы можете написать

error();
return;

Но вы можете пересмотреть свой подход.Это может быть не совсем объектно-ориентированный.

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

Ваша функция когда-нибудь что-нибудь возвращает? Если вы не хотите, чтобы ваша функция была недействительной, кажется:

void fun() {

...


}
...