Почему у Java есть `void` методы?

51

Нужно ли / почему Java иметь voidметоды? Ссылка :

Любой метод, объявленный как void, не возвращает значение.

Насколько я могу судить, каждое использование voidбыло бы лучше обслуживать, возвращая флаг состояния, вызываемый объект или null.

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

marisbest2
источник
Так что нам не нужен специальный синтаксис для написания подпрограмм. Мы можем использовать одну и ту же функцию.
candied_orange

Ответы:

158

Потому что есть разница между «Эта функция может быть успешной или неуспешной и достаточно самосознательна, чтобы она могла отличить» и «Нет отзывов об эффекте этой функции». Без voidэтого вы бы бесконечно проверяли коды успеха и полагали, что вы пишете надежное программное обеспечение, хотя на самом деле вы ничего не делаете.

Килиан Фот
источник
10
Но было ли это успешно или неудачно - это то, что нужно выразить, выбрасывая или не выбрасывая исключение, не возвращая флаг. И возвращение voidничего не говорит о том, достаточно ли самодостаточен метод или функция, чтобы бросить в зависимости от ситуации.
Фолькер Сигел
12
@VolkerSiegel: контекст ответа - это вопрос. ОП предположил, что во всех случаях, когда используется void, функция должна вместо этого возвращать флаг состояния, указывающий на успешность сбоя. В этом контексте возвращение void действительно говорит, что функции буквально нечего возвращать. Если принудительно заставить такую ​​функцию всегда возвращать 0, чтобы указать успешность, пользователи этой функции будут ложно уверены, что они проверяют ошибки, когда в действительности возвращаемое значение всегда равно 0 независимо от успеха или неудачи.
slebetman
19
@VolkerSiegel Существует большое количество ситуаций, когда исключения не являются правильным способом выполнения действий. На самом деле, я бы сказал, что исключение скорее правильно, чем неправильно - исключение означает, что произошло нечто ужасное, что необходимо учитывать. В случае ожидаемого сбоя никогда не следует использовать исключение.
Гейб
35
@GabeSechan Нет, исключения должны генерироваться, если метод не может выполнить свой контракт. Если метод требует ненулевую ссылку, но он получает ее, это ожидаемый сбой, и он должен выдать.
Энди
5
Есть много альтернативных синтаксисов, чтобы это исправить. Причина, по которой они выбрали это (неудобное) решение, заключается в том, что C использует его.
Марко ван де
95
  1. Потому что C имеет voidтип, а Java был разработан, чтобы следовать многим соглашениям семейства языков C.
  2. Есть много функций, которые вы не хотите возвращать. Что вы собираетесь делать с «универсальным Successтипом» в любом случае? Фактически, возвращаемые значения, указывающие на успех, в Java даже менее важны, чем в C, потому что Java имеет исключения, указывающие на сбой, а C - нет.
Мейсон Уилер
источник
8
> "А что ты собираешься делать с" универсальным типом успеха "?" - при написании универсального кода один тип значения (он называется «Тип модуля») очень полезен, поскольку исключает особый случай функций, которые «просто возвращают», но не возвращают ничего конкретного. Но когда Java была впервые создана, она имела еще менее выразительную систему типов (без параметров типов), поэтому практических различий не было. А сейчас уже поздно меняться. Более современные языки фактически избегают void «тип» (на самом деле это даже не тип, а просто специальный маркер для методов) и вместо этого используют тип Unit.
Sarge Borsch
9
@SargeBorsch Java- Voidтип действует как тип Unit (например, для Generics), но, к сожалению, он отличается от void(примечание: приятный котенок умирает каждый раз, когда вы изобретаете новый тип, чтобы исправить тип, который вы испортили в предыдущей версии). Если кто-то не знаком с типами юнитов, это
неплохое
1
@ OgrePsalm33 на самом деле он не действует как тип Unit (по крайней мере, в практических целях - все равно нужно написать «return null;» для возврата из метода, который имеет тип возврата Void, а компилятор AFAIK действительно выделяет место в стеке для ссылок Void). не пользуясь тем фактом, что читать эти значения бессмысленно), это просто соглашение использовать его там, где «правильный» тип модуля будет более подходящим.
Sarge Borsch
3
@immibis, потому что тип Unit по определению должен иметь ровно одно значение, а Void не имеет значений. Да, его можно использовать null(на самом деле это единственный выбор), но ноль - это особая вещь, любое значение ссылочного типа в Java может быть нулевым (это еще одна ошибка в проектировании языка). И, как я уже говорил ранее, если вы используете Voidтип возврата вместо voidmark, компилятор Java не ваш друг.
Сардж Борщ
5
@SargeBorsch Пустота имеет ровно одно значение, которое null. Тот факт, что он nullявляется членом большинства типов, не имеет отношения к тому, Voidявляется ли это типом единицы.
user253751
65

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

Это была первоначальная причина.

Твои предложения:

возвращение флага статуса

Это нет-нет. Мы не используем статусные флаги. Если что-то идет не так, мы сообщаем об этом через исключения.

вызываемый объект

Свободный стиль вызовов - недавняя разработка. Многие из программистов, которые сегодня с радостью используют свободное владение языком и закатывают глаза, если им когда-либо приходится использовать интерфейс, который его не поддерживает, даже не были рождены при создании Java.

возвращая ноль

Это вынудит вас объявить метод как возвращающий что-то, когда на самом деле он ничего не возвращает, так что это будет очень запутанным для того, кто смотрит на интерфейс, пытаясь понять, что он делает. Люди неизбежно придумают какой-то бесполезный в противном случае класс, который будет означать «нет возвращаемого значения», чтобы все функции, которым нечего возвращать, могли возвращать нулевую ссылку на такой класс. Это было бы неуклюже. К счастью, есть решение для этого, это называется void. И это ответ на ваш вопрос.

Майк Накис
источник
3
Пожалуйста, предоставьте источник для "Первопричины ...". Насколько мне известно, настоящая причина в том, что C был разработан, чтобы быть портативным ассемблером.
Торбьерн Равн Андерсен
6
@ ThorbjørnRavnAndersen вы действительно спрашиваете меня, чтобы предоставить источник для утверждения, что синтаксис Java является производным от синтаксиса C?
Майк Накис
3
@ ThorbjørnRavnAndersen о, я понимаю, я не понял. Итак, вы хотите источник моего утверждения о C, а не о Java. Хорошо, извините, у меня нет источника для этого. Но я думаю, что это довольно очевидно. (Раньше я программировал на Паскале в 1987 году, когда я переключился на C. Боже мой, это 30 лет назад.)
Майк Накис,
6
Это не очевидно. C был разработан примерно в то же время, что и паскаль, людьми, которые, скорее всего, даже не знали о его существовании. Пожалуйста, не высказывайте предположения как факты.
Торбьерн Равн Андерсен
12
Реальная первоначальная причина в том , что по умолчанию функция C возвращается int, поэтому ключевое слово было добавлено после K & R для указания «возвращаемого типа».
user207421
20

Некоторые методы, вроде System.out.println, не возвращают ничего полезного, но вызываются исключительно для этого побочного эффекта. voidявляется полезным индикатором для компилятора и читателя кода, что никакое полезное значение не возвращается.

Возврат nullвместо voidозначает, что вы просто получите NullPointerExceptionмомент, когда вы используете это значение для чего-либо. Таким образом, вы обмениваете ошибку времени компиляции на ошибку времени выполнения, которая намного хуже. Кроме того, вы должны будете определить тип возвращаемого значения как Object, что будет вводить в заблуждение и сбивать с толку. (И цепочка все равно не сработает.)

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

Возврат thisневозможен из статических методов.

Возвращение универсального Successобъекта не имело бы никакой полезной цели.

JacquesB
источник
1
Полностью с вами согласен, самый простой и лаконичный ответ.
webo80
11

Одна из причин заключается в том, что вводить в заблуждение что-либо, кроме, скажем, неверно null. Пример:

Что должно Arrays.sort(a)вернуться?

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

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

И если вы возвращаете что-то совершенно абсурдное (например, длину a), то это просто сбивает с толку использование этого возвращаемого значения - просто подумайте, насколько более запутанным будет сказать int len = Arrays.sort(a)вместо int len = A.length!

Mehrdad
источник
1
Честно говоря, программист не обязательно должен был бы писать return null;- JVM также может предписывать, что если управление выходит за пределы функции, отличной от явного returnили throwоператора, nullнеявно возвращается вызывающей стороне. IIRC C делает это, за исключением того, что возвращаемое значение в этом случае не определено (это будет любое значение, которое будет в местоположении, используемом для возвращаемого значения в данный момент).
CVn
2
Правильный ответ на ваш жирный вопрос - «отсортированный массив».
Брайан
2
@BryanBoettcher: Вы не читали остальную часть этого?
Мердад
1
@Michael Kjörling: это фундаментальное свойство языка Java - предотвращать такие ошибки, т.е. не иметь «неопределенного поведения», если вы забыли returnутверждение. Вместо этого вы получаете ошибку компилятора, сообщающую о вашей ошибке. Вставка неявного return null;было бы лучше, чем «неопределенное поведение», но не намного. Это было бы полезно только в том случае, если возвращаемый тип не имеет значения, но откуда компилятор делает вывод, что возвращаемое значение не имеет значения, если это не так void?
Хольгер
2
@ MichaelKjörling: «Если returnдействительно не добавляет никакого значения…», ну, как уже говорилось, для компилятора нет индикатора, что возвращение не добавит никакого значения, если нет voidтипа. На самом деле все наоборот, первое предположение состоит в том, что метод, объявляющий возвращаемый тип, хочет returnчего-то полезного этого типа. И мы еще не говорили о примитивных типах, у которых нет nullзначения, следовательно, любое значение по умолчанию, введенное компилятором, будет конфликтовать с диапазоном потенциально полезных возвращаемых значений.
Хольгер
7

Возвращать a, booleanвсегда равное тому, trueкоторое вы предлагаете, не только бессмысленно (возвращаемое значение не несет никакой информации), но на самом деле вводит в заблуждение. Большую часть времени, то лучше использовать типы возвращения , которые только несут столько информации , сколько на самом деле доступны - вот почему в Java у вас есть booleanдля true/ , falseа не возвращая intс 4 миллиардов возможных значений. Аналогично, возвращение a booleanс двумя возможными значениями, в которых есть только одно возможное значение («общий успех»), будет вводить в заблуждение.

Это также добавило бы ненужные потери производительности.

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

Одной вещью, которая может быть улучшена, будет создание voidфактического типа, такого как Scala Unit. Это решило бы некоторые проблемы, такие как обработка дженериков.

Михал Космульский
источник
Забавный факт: List.addвсегда возвращается true, но это для совместимости с более широким контрактом Collection.add, поэтому Set.addможет вернуться trueили false.
Хольгер
4

Ява - относительно старый язык. А в 1995 году (когда он был создан) и вскоре после этого программисты были очень обеспокоены количеством времени, которое процесс взял под контроль процессора, а также потреблением памяти. Возврат void устранит несколько тактов и немного потребления памяти при вызове функции, потому что вам не нужно будет помещать возвращаемое значение в стек, и вам впоследствии не придется его удалять.

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

Ленивый Кодер
источник
14
программисты, которые были очень обеспокоены скоростью, не будут использовать Java в наши дни, потому что первые реализации Java были общеизвестно медленными. Настолько медленно, что многие люди, которые в настоящее время не работают с ним, все еще находятся под впечатлением, что Java является синонимом жирного и медленного.
Sarge Borsch
8
@SargeBorsch напоминает мне старую программную шутку 20-летней давности. "Тук-тук." "Кто здесь?" ... ... ... ... очень длинная пауза ... ... ... "Ява"
Дэн Нили
4
@DanNeely, а через некоторое время машина с ИИ врезается в стену на полной скорости, даже не пытаясь использовать тормоза. Это было написано на Java. Итак, Java сейчас не тормозит! (это был каламбур на русском языке, не очень хорошо переводит его на английский)
Sarge Borsch
2
@SargeBorsch То, что у вас есть, работает как английский каламбур, даже если он потерял некоторый нюанс в переводе. И перевод каламбур, вероятно, сложнее, чем поэзия.
Дэн Нили,
3
Хорошие программисты по- прежнему озабочены производительностью и потреблением памяти, что делает ненужным просить людей возвращать мусор даже сегодня.
Sklivvz
2

Я прочитал аргументы, которые предполагают, что второй вариант (возврат this) должен быть подход вместо void. Это довольно хорошая идея, но подход в стиле конструктора не был популярен во время создания Java. Если бы это было так же популярно в то время, как и сейчас, возможно, таков подход. Возвращение null- это действительно плохая идея ИМО. Хотелось бы nullдаже не на языке вообще.

Проблема в том, что если метод возвращает экземпляр своего собственного типа, неясно, является ли это новым объектом или тем же самым. Часто это не имеет (или не должно) иметь значение, но в других случаях это имеет значение. Я полагаю, что язык можно изменить, чтобы неявно возвращаться thisк пустым методам. Единственная проблема, о которой я могу подумать в данный момент, заключается в том, что если бы метод был позже изменен для возврата нового объекта того же типа, не было бы предупреждений компиляции, но это, возможно, не имеет большого значения. Нынешняя система типов не заботится об этих типах изменений сейчас. То, как это взаимодействует с наследованием / интерфейсами, потребует некоторого рассмотрения, но это позволило бы легко вызывать старые API без стиля конструктора, как если бы они это делали.

JimmyJames
источник
2
@ Коди Хорошо, это не относится к статическим методам.
JimmyJames
14
@ marisbest2 Какой аргумент?
8bittree
3
Ну, если бы nullони не были частью языка, люди использовали бы все виды дозорных значений, которые означают «пожалуйста, игнорируйте этот аргумент / возвращаемое значение, оно не содержит никакого разумного значения». Проблема nullне в самой вещи, а в том, что она используется неправильно. Держу пари, что эта проблема будет только преувеличена, если стандартизированный nullбудет заменен несметным числом пользовательских решений, где каждая реализация будет вести себя по-разному, как молчаливое игнорирование использования, или выбрасывать специальные исключения вместо стандартного исключения нулевого указателя и т. Д.
cmaster
2
@cmaster очевидной заменой nullявляется правильный необязательный тип (например, Maybeв Haskell). Проблема с нулями в Java состоит в том, что нет никакого здравого способа немедленно решить, является ли значение обнуляемым на практике или нет. Это приводит к обоим из: излишне защитному программированию (проверка на нули, где это бессмысленно, увеличивая тем самым процент корма в коде), и случайным NPE в производстве (если забыли проверить, где это было необходимо). Да, это частично решается аннотациями, но они являются необязательными, не всегда проверяются и т. Д. Таким образом, они не спасут вас на границах с сторонним кодом.
Сардж Борщ
2
@SargeBorsch Я согласен с этим :-) Мое возражение против языка без стандартного способа выразить «пожалуйста, игнорируйте это значение». nullотлично работает для этого (отлично = просто), но стандартизированные необязательные значения с твердой семантикой, безусловно, еще один хороший вариант (хорошо = безопасно).
cmaster
2

Еще один аспект:

Java - это статический язык с довольно строгими возможностями. Это означает, что многие вещи, которые были бы достаточно открыты или динамичны в других языках (c / f Ruby, Lisp и т. Д.), Строго определены.

Это общее дизайнерское решение. На «почему» сложно ответить (ну, потому что разработчики языка думали, что это будет хорошо!). «Зачем» довольно ясно: он позволяет компилятору обнаруживать множество ошибок, что, как правило, довольно хорошая функция для любого языка. Во-вторых, это сравнительно легко рассуждать о языке. Например, относительно легко создать формальную корректность, доказывающую в (подмножествах) язык Java; для сравнения, это было бы практически невозможно в динамическом языке, таком как Ruby et al.

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

Так. Наличие строгого voidтипа - ясное сообщение: этот метод ничего не возвращает, точка. Что есть, то есть. Замена его принудительным постоянным возвратом чего-либо привела бы к гораздо более динамичному поведению (как в Ruby, где каждое определение всегда имеет явное или неявное возвращаемое значение), что было бы плохо для доказуемости и рассуждений; или к огромному раздутию, используя какой-то другой механизм здесь.

(И NB, Ruby (например) обрабатывает это по-другому, и его решение все еще точно так же приемлемо, как и у Java, потому что у него совершенно иная философия. Например, оно полностью исключает доказуемость и разумность, одновременно уделяя большое внимание чрезвычайно высокая выразительность языка.)

Anoe
источник