Почему я не могу явно вернуть void из метода?

void run() {
    ...
    if (done) return cancel();
    ...
}

где cancel() возвращает void. Это не скомпилируется... и я почти понимаю, почему. Но если я хочу вернуть пустоту из пустоты, почему бы и нет? Вместо этого я заканчиваю тем, что пишу что-то вроде этого:

if (done) {
    cancel();
    return;
}

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


person Travis Webb    schedule 17.10.2011    source источник
comment
Естественно, функции void не предоставляют значение результата вызывающей стороне.   -  person Eder    schedule 17.10.2011
comment
Я думаю, что единственный возможный ответ на вопрос «почему»: спросите дизайнера языка.   -  person NPE    schedule 17.10.2011
comment
Метод не «возвращает пустоту». Метод не имеет возвращаемого типа.   -  person user207421    schedule 18.10.2011
comment
(void) в объявлении функции означает, что функция не имеет возвращаемого значения.   -  person Don Gillespie    schedule 19.10.2011


Ответы (14)


Оператор return с выражением возвращает значение этого выражения. Тип cancel() является пустым выражением — оно не имеет значение.

Логически вы хотите выполнить cancel(), а затем вернуться - вот что вы должны сказать. Два действия (вызов cancel() и затем возврат) логически различны.

Теперь Java может иметь что-то вроде типа "unit" вместо void, но это повлияет на а не просто возвращаемые значения.

person Jon Skeet    schedule 17.10.2011
comment
java.lang.Void можно использовать для этого (см. мой ответ ниже) - person michael667; 17.10.2011
comment
@ michael667, но Пустота не пуста. Интерфейс, возвращающий void, не может быть реализован методом, возвращающим java.lang.Void. java.lang.Void полезен только для обработки void в Reflection и Generics. - person josefx; 18.10.2011

Это интересный вопрос. Поскольку java применяет тип возвращаемого значения (void — тип возвращаемого значения), ваше первое утверждение кажется логичным. Я бы принял это только за условность. Поскольку void является заполнителем, а не объектом, вероятно, было решено не использовать его для согласованности языка или простоты компилятора.

Из JLS

Оператор return без Expression должен содержаться в теле метода, объявленного с использованием ключевого слова void, не возвращающего никакого значения (§8.4), или в теле конструктора (§8.8).

дальше

Чтобы быть точным, оператор return без Expression всегда завершается внезапно, причиной является возврат без значения.

person Johan Sjöberg    schedule 17.10.2011
comment
Согласитесь с @Johan, что такое поведение соответствует стандарту. Но я бы сказал, что было бы совершенно естественно разрешить return cancel() в качестве синонима cancel(); return или даже void result = cancel(); return result. - person MarnixKlooster ReinstateMonica; 18.10.2011

Это как писать:

void v = (void) 1;
return (v);

Итак, я думаю, что void не является type в Java. В C++ допустимо return cancel();. Как программист на C++, знакомый с Java, я могу ответить так: многие вещи не поддерживаются в синтаксисе Java. Может быть, для простоты или читабельности.

Примечание. Объявление void f() похоже на объявление procedure f() в паскале, и процедура не может возвращать никаких значений, таких как функции, поэтому мы должны вызывать их в отдельном операторе.

person masoud    schedule 17.10.2011

void не является типом. Если вы используете тип Void вместо void, ваш код будет работать, но: вам придется вручную return null во всех точках выхода из вашего метода.

person michael667    schedule 17.10.2011
comment
Истинный. Единственное место, где я использую тип Void, это Callables - person michael667; 17.10.2011
comment
+1 за этот ответ (и другие), потому что это попадает в самую точку: в Java void не является типом, а скорее указывает на отсутствие какого-либо типа. Однако нет никаких причин, по которым void нельзя было бы использовать для представления «типа, который не содержит значений». (Scala делает это и называет этот тип Unit.) - person MarnixKlooster ReinstateMonica; 18.10.2011
comment
Отдельное спасибо за объяснение Void (с большой буквы V) - person Gyro Gearloose; 08.11.2019

Потому что вы не вернете void. void не является значением, поэтому его нельзя вернуть.

person Bozho    schedule 17.10.2011

Это тавтология. Это означает, что void определяет, что метод не имеет возвращаемого значения. Следовательно, как вы можете «вернуть пустоту», если пустота вообще не является возвратом?

person Emoire    schedule 17.10.2011
comment
@psr: Конечно, можно. Но мы не говорим о компиляторе, который делает это. И почему вы написали бы тот, который работал таким образом? - person Emoire; 04.11.2011
comment
Знаешь, я не могу вспомнить, о чем я думал, когда писал это. Кажется, я думал, что это не тавтология. Но я полагаю, вы имели в виду истину по определению в Java, что так и есть. Что можно назвать тавтологией. Может быть, я думал, что, похоже, нет никакого вреда в том, чтобы разрешить возврат void, который будет таким же, как просто возврат, который позволит использовать тот же синтаксис при вызове метода, который не возвращает значение (как в ОП) как при вызове того, кто делает. Это означает, что это только тавтология в контексте Java, которая странно не тавтологична. - person psr; 04.11.2011

Краткий ответ

Оператор return cancel() должен возвращать допустимое значение, но объявление метода void run() объявляет, что run() не возвращает значение; следовательно, return cancel() в run() является ошибкой. Оператор return (без выражения) пытается передать управление вызывающему объекту и используется, когда тип возвращаемого значения метода равен void; следовательно, не ошибка.

Длинный ответ

JLS The *return* Statement указано:

Оператор return без выражения пытается передать управление инициатору метода или конструктора, который его содержит. [...] Оператор возврата с выражением должен содержаться в объявлении метода, который объявлен для возврата значения (§8.4), иначе возникает ошибка времени компиляции. Выражение должно обозначать переменную или значение некоторого типа T, иначе произойдет ошибка времени компиляции. Тип T должен быть присваиваемым (§5.2) объявленному типу результата метода, иначе возникает ошибка времени компиляции.

В разделе Method Return Type JLS указано:

Тип возвращаемого значения метода объявляет тип значения, которое возвращает метод, если он возвращает значение, или указывает, что метод недействителен. Объявление метода d1 с типом возвращаемого значения R1 является замещаемым по типу возвращаемого значения другим методом d2 с типом возвращаемого значения R2 тогда и только тогда, когда выполняются следующие условия: [...] * Если R1 недействителен, то R2 недействителен.

JLS Types, Values, and Variablesглава, первый абзац гласит:

Язык программирования Java является строго типизированным языком, что означает, что каждая переменная и каждое выражение имеют тип, который известен во время компиляции. Типы ограничивают значения, которые может содержать переменная (§4.12) или которые может создавать выражение, ограничивают операции, поддерживаемые этими значениями, и определяют значение операций.

В разделе The Kinds of Types and Values JLS указано:

В языке программирования Java существует два вида типов: примитивные типы (§4.2) и ссылочные типы (§4.3). Соответственно, существует два типа значений данных, которые могут храниться в переменных, передаваться в качестве аргументов, возвращаться методами и обрабатываться: примитивные значения (§4.2) и ссылочные значения (§4.3).

Теперь еще несколько цитат. В разделе Expression Statements JLS указано:

В отличие от C и C++, язык программирования Java позволяет использовать в качестве операторов выражений только определенные формы выражений. Обратите внимание, что язык программирования Java не допускает «приведения к пустоте» — void не является типом

В разделе Method Body JLS указано:

Если метод объявлен недействительным, то его тело не должно содержать оператора возврата (§14.17), содержащего выражение.

И, наконец, в разделе JLS Method Declarations говорится :

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

Теперь, когда мы собираем все это вместе, мы можем сделать следующий вывод:

  • Если оператор return содержит выражение, выражение должно оцениваться как допустимое значение.
  • Допустимое значение выражения return должно быть примитивным или ссылочным типом.
  • void не является допустимым типом значения.
  • Метод, объявленный с типом возвращаемого значения void, не возвращает значения.
  • Метод void run() не возвращает значение.
  • В run() return без выражения с радостью передаст управление вызывающей стороне.
  • В run() return some expression является ошибкой, поскольку some expression должно быть допустимым значением, а run() не возвращает значение.
person Dan Cruz    schedule 18.10.2011

return x явно означает «вернуть значение x», независимо от того, какой это тип (тип, конечно, по-прежнему должен соответствовать возвращаемому типу любой функции, в которую помещен оператор).

void — это, строго говоря, отсутствие типа и, в более широком смысле, отсутствие значения, поэтому возвращать его не имеет смысла, как и не имеет смысла (и не является разрешено) для объявления переменной void.

person Michael Madsen    schedule 17.10.2011

Void не является реальным типом. Void — это просто заполнитель, чтобы сделать синтаксис определения методов более последовательным. Это не инновация Java; это унаследовано от C.

По этой причине компилятор не позволяет вам писать return cancel(), даже если метод cancel() является void.

person AlexR    schedule 17.10.2011
comment
Из-за вашего грамматического типа заглавных букв ваш ответ неверен. Вы хотели сказать, что void не является реальным типом, потому что Void наверняка настоящий тип. - person Ryan Stewart; 17.10.2011

void не является типом. void в определении метода — это просто заполнитель, который ничего не возвращает.

person Sahil Muthoo    schedule 17.10.2011

Интересная идея. Основная проблема заключается в спецификации языка, которая определяет оператор return как состоящий из return <expression>. Метод void не является выражением, поэтому конструкция не разрешена.

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

person dlev    schedule 17.10.2011

Из JLS:

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

...

Оператор return с Expression должен содержаться в объявлении метода, объявленного для возврата значения, иначе произойдет ошибка времени компиляции. Выражение должно обозначать переменную или значение некоторого типа T, иначе произойдет ошибка времени компиляции. Тип T должен быть присваиваемым объявленному типу результата метода, иначе произойдет ошибка времени компиляции.

person Prince John Wesley    schedule 17.10.2011

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

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

person acjay    schedule 17.10.2011

Правильный способ справиться с этим:

void run() {
...
if (done) {
    cancel();
    return;
    }
...
}
person djhaskin987    schedule 19.10.2011