Чому у Java є `void` методи?


51

Чи / для чого Java потрібно мати voidметоди? Довідка :

Будь-який метод, оголошений недійсним, не повертає значення.

Наскільки я можу подумати, кожне використання voidбуло б краще обслуговуватися шляхом повернення прапора статусу, об'єкта, на який викликається, або null.

Це зробило б кожен виклик заявою, яка може бути присвоєною, та полегшила б шаблони розробників та ланцюжок методів. Методи, на які посилаються лише їх ефекти, як правило, повертають булевий або загальний Successтип або викидають виняток у разі відмови.


Так що нам не потрібен спеціальний синтаксис для написання підпрограм. Ми можемо використовувати однакові функції.
candied_orange

Відповіді:


158

Тому що існує різниця між "Ця функція може успішно перемогти або вийти з ладу і є достатньо усвідомленою, що може сказати різницю" та "Немає відгуків про ефект цієї функції". Без цього voidви будете нескінченно перевіряти коди успіху і вважаєте, що ви пишете надійне програмне забезпечення, коли ви насправді нічого не робите.


10
Але вдалось це чи не вдалося - щось висловити, кинувши чи не кинувши виняток, не повернувши прапор. А повернення voidнічого не говорить про те, чи метод чи функція є достатньо усвідомленими, щоб кинути як слід.
Volker Siegel

12
@VolkerSiegel: контекст відповіді - це питання. ОП запропонувало, щоб у всіх випадках, коли використовується недійсність, функція повинна замість цього повертати прапор статусу, що вказує на успіх відмови. У цьому контексті повернення порожнечі дійсно говорить про те, що функція буквально нічого не повертає. Примушення такої функції завжди повертати 0, щоб вказати на успіх, дасть користувачам функції помилкову впевненість, що вони перевіряють помилки, коли насправді значення повернення завжди дорівнює 0, незалежно від успіху чи невдачі.
slebetman

19
@VolkerSiegel Існує велика кількість ситуацій, коли винятки не є правильним способом вчинити. Насправді я б сказав, що рідше виняток є правильним, ніж неправильним; виняток означає щось жахливе, що потрібно враховувати. Очікуваний випадок відмови ніколи не повинен використовувати виняток.
Гейб Сечан

35
@GabeSechan Ні, винятки слід кидати, якщо метод не в змозі виконати свій договір. Якщо метод вимагає невдалого посилання, але він отримує його, це очікуваний збій, і він повинен кинути.
Енді,

5
Існує багато альтернативних синтаксисів, щоб виправити це. Причина, що вони обрали це (незручне) рішення, полягає в тому, що C використовує його.
Марко ван де Ворт

95
  1. Оскільки C має voidтип, а Java створена для дотримання багатьох конвенцій сімейства мов C.
  2. Існує багато функцій, які не хочуть повертати значення. Що ви все одно будете робити з "родовим Successтипом"? Насправді повернені значення для вказівки на успіх є навіть менш важливими для Java, ніж для C, оскільки у Java є винятки, які свідчать про збій, а C - ні.

8
> "Що ви все одно будете робити з" загальним типом успіху "?" - при написанні загального коду один тип значення (він називається Unit type btw) дуже корисний, оскільки він виключає особливий випадок функцій, які "просто повертаються", але нічого не повертають зокрема. Але коли Java була створена вперше, вона має ще менш виразну систему типу (без параметрів типу), тому практичної різниці не було. І тепер уже пізно міняти. Більш сучасні мови фактично уникають недійсного "типу" (насправді це навіть не тип, а лише спеціальний маркер для методів) і використовують замість нього тип одиниці.
Sarge Borsch

9
@SargeBorsch VoidТип Java діє як тип одиниці (наприклад, для Generics), хоча, на жаль, вона відрізняється від void(бічна примітка: милий кошеня вмирає кожен раз, коли винайдете новий тип, щоб виправити тип, який ви зіпсували в попередній версії). Якщо хтось не знайомий з типами одиниць, це гідне вступ: en.wikipedia.org/wiki/Unit_type
Ogre Psalm33

1
@ OgrePsalm33 він насправді не виступає типом Unit (принаймні, на практичному рівні - все одно потрібно написати "return null;", щоб повернутися з методу, який має тип return void, а компілятор AFAIK виділяє простір у стеку для Void посилань , не скориставшись тим, що ніколи не має сенсу читати ці значення), це лише умова використовувати його там, де «правильний» тип одиниці був би більш підходящим.
Sarge Borsch

3
@immibis, оскільки тип Unit за визначенням повинен мати точно одне значення, а Void не має значень. Так, це можливо використовувати null(насправді це єдиний вибір), але null - особлива річ, будь-яке значення посилального типу на Java може бути нульовим (це ще одна помилка в мовному дизайні). І, як я вже говорив раніше, якщо ви використовуєте Voidтип повернення замість voidпозначки, компілятор Java не є вашим другом.
Sarge Borsch

5
@SargeBorsch Void має саме одне значення, яке є null. Те, що nullє членом більшості типів, не має значення для того, чи Voidє одиничним типом.
користувач253751

65

Оригінальна причина , чому мова має voidтип, тому що , як і в C, творці мови не хочуть зайве ускладнити синтаксис мови з процедурою з і функцією и шляхом Паскаль зробив.

Це була первісна причина.

Ваші пропозиції:

повернення прапора статусу

Це ні-ні. Ми не використовуємо прапори статусу. Якщо щось піде не так, ми повідомляємо про це через винятки.

об'єкт, на який викликається

Вільний стиль викликів - це нещодавній розвиток. Багато програмістів, які сьогодні дуже щасливо безперешкодно користуються та заплющують очі, якщо їм коли-небудь доведеться користуватися інтерфейсом, який не підтримує його, навіть не народилися під час створення Java.

повернення нуля

Це змусить вас оголосити метод як повернення чогось, коли насправді він нічого не повертає, тому було б дуже заплутаним той, хто дивиться на інтерфейс, намагаючись зрозуміти, що він робить. Люди неминуче вигадують якийсь інакше непридатний клас, який розшифровується як "не повертається значення", так що всі функції, які не мають нічого повернути, можуть повернути нульове посилання на такий клас. Це було б незграбно. На щастя, для цього є рішення, воно називається void. І це відповідь на ваше запитання.


3
Будь ласка, вкажіть джерело для "Первісна причина ...". Наскільки мені відомо, справжня причина полягає в тому, що C був розроблений як портативний асемблер.
Thorbjørn Ravn Andersen

6
@ ThorbjørnRavnAndersen Ви насправді, насправді, просите мене надати джерело твердження про те, що синтаксис Java походить від синтаксису С?
Майк Накіс

3
@ ThorbjørnRavnAndersen о, бачу, я зрозумів неправильно. Отже, ви хочете джерело моїх тверджень про C, а не про Java. Гаразд, вибачте, у мене немає для цього джерела. Але я думаю, що це досить очевидно. (Раніше я програмував у Паскалі в 1987 році, коли я перейшов на C. Моє добро, це було 30 років тому.)
Майк Накіс,

6
Це не очевидно. C був розроблений приблизно в той самий час, коли паскаль людей, які, швидше за все, навіть не знали, що він існує. Будь ласка, не зазначайте припущення як факти.
Thorbjørn Ravn Andersen

12
Реальна першопричини в тому , що за замовчуванням функції C повертається int, тому ключове слово було додано після K & R для вказівки «повертається типу».
користувач207421

20

Деякі методи, як-от System.out.println, не повертають нічого корисного, але називаються виключно для цього побічного ефекту. voidє корисним показником для компілятора і для читача коду, що корисне значення не повертається.

Повернувшись nullзамість voidзасобів, ви просто отримаєте NullPointerExceptionмить, коли використаєте це значення для чого-небудь. Таким чином, ви торгуєте помилкою компіляції за помилку виконання, що значно гірше. Крім того, вам доведеться визначити тип повернення як такий Object, що вводить в оману та заплутано. (І прикувати все одно не вийде.)

Коди статусу зазвичай використовуються для вказівки на умови помилок, але у Java для цього є винятки.

Повернення thisнеможливо статичними методами.

Повернення родового Successоб’єкта не мало б корисної мети.


1
Повністю згоден з вами, найпростіша і стисла відповідь.
webo80

11

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

Що слід Arrays.sort(a)повернути?

Якщо ви стверджуєте, що його aслід повернути, щоб виклик був ланцюговим (що, здається, є вашою відповіддю, судячи з вашого запитання), то вже не зрозуміло, чи є повернене значення копією оригінального об'єкта, або самим оригінальним об'єктом . І те й інше можливо. Так, ви можете це помістити в документацію, але це досить неоднозначно, що не слід створювати неоднозначність в першу чергу.

З іншого боку, якщо ви стверджуєте, що його nullслід повернути, то виникає питання про те, яку інформацію повернення значення, можливо, забезпечує абонент, і чому програміст повинен змушений писати, return nullколи він не передає ніякої інформації.

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


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

Повернення, booleanяке завжди дорівнює тому, trueщо ви пропонуєте, не тільки безглуздо (повернене значення не містить інформації), але й насправді вводить в оману. Велику частину часу, то краще використовувати повертаються типи , які тільки несуть стільки інформації , скільки насправді доступні - ось чому в Java у вас є booleanдля true/ , falseа не повертаючи intз 4 мільярдів можливих значень. Так само, повернення а booleanз двома можливими значеннями, де існує лише одне можливе значення ("загальний успіх"), було б оманливим.

Це також додасть непотрібних накладних витрат.

Якщо метод використовується лише для його побічного ефекту, повернути нічого немає, а тип повернення voidвідображає саме це. Про можливі рідкісні помилки можна сигналізувати за винятком.

Одне, що можна було б вдосконалити, - це створення voidфактичного типу, наприклад, Scala Unit. Це дозволить вирішити деякі проблеми, такі як обробка дженериків.


Факт забави: List.addзавжди повертається true, але це для того, щоб бути сумісним з більш широким контрактом Collection.add, тому Set.addможе повернутися trueабо false.
Холгер

4

Java - відносно стара мова. А в 1995 році (коли він був створений) і незабаром після цього програмісти були дуже стурбовані тим, скільки часу процес взяв контроль над процесором, а також витратою пам'яті. Повернення порожнечі призвело до усунення декількох тактових циклів та трохи споживання пам'яті з виклику функції, оскільки вам не доведеться ставити значення повернення на стек, і згодом вам не доведеться його видаляти.

Ефективний код не повертає вам те, що ви ніколи не використовуватимете, а значить, пустота в щось, що має безглузде значення повернення, є набагато кращою практикою, ніж повернення значення успіху.


14
програмісти, які були дуже стурбовані швидкістю, в будь-який час не використовуватимуть Java, тому що перші впровадження Java були, як відомо, повільними. Настільки повільно, що багато людей, які зараз не працюють з цим, все ще залишаються під таким враженням, що Java є синонімом жиру та повільності.
Sarge Borsch

8
@SargeBorsch нагадує мені старий жарт з програмування 20 років тому. "Стук-стук." "Хто там?" ... ... ... ... дуже довга пауза ... ... ... "Ява"
Ден Нілі

4
@DanNeely і через деякий час автомобіль, керований AI, врізається в стіну на повній швидкості, навіть не намагаючись використовувати гальмо. Це було написано на Java. Отже, Java не гальмує зараз! (це було каламбур російською мовою, не можу дуже добре перекласти його на англійську)
Sarge Borsch

2
@SargeBorsch Те, що у вас є, працює як англійський каламбур, навіть якщо він втратив певний нюанс у перекладі. А перекласти каламбури, мабуть, складніше, ніж поезію.
Dan Neely

3
Хороші програмісти все ще переймаються продуктивністю та витрачанням пам’яті, що змушує зайве просити людей повертати сміття, актуальне навіть сьогодні.
Sklivvz

2

Я читав аргументи, які підказують, що другим варіантом (поверненням this) повинен бути підхід замість void. Це досить гарна ідея, але підхід у стилі будівельника не був популярним у той час, коли створювалася Java. Якби вона була настільки популярною в той час, як зараз, це, можливо, був би такий підхід. Повернення null- це дуже погана ідея ІМО. Мені б хотілося, щоб nullнавіть не було мови.

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


2
@Cody Добре, що це не стосується статичних методів.
JimmyJames

14
@ marisbest2 Який аргумент?
8bittree

3
Ну, якби nullне частина мови, люди використовували б усілякі значення дозорних значень, які означають "будь ласка, ігноруйте цей аргумент / повертайте значення, воно не містить жодного розумного значення". Проблема nullне в тому, що вона сама полягає в тому, що вона, як правило, використовується неправильно. Я б загрожував, що ця проблема буде перебільшена лише в тому випадку, якщо стандартизовані nullзамінюватимуться безліччю спеціальних рішень, де кожна реалізація поводитиметься по-різному, як мовчазне ігнорування використання, або викидання спеціальних винятків замість стандартного виключення з нульовим вказівником тощо.
cmaster

2
@cmaster очевидною заміною для nullналежного додаткового типу (наприклад, Maybeу Haskell). Проблема з нулями в Java полягає в тому, що немає жодного розумного способу негайно вирішити, чи є значення нульовим на практиці чи ні. Це призводить до обох зайвих оборонних програмувань (перевірка нулей там, де це безглуздо, тим самим збільшуючи відсоток пуска в коді), і випадкових NPE у виробництві (якщо забули перевірити, де це потрібно). Так, це частково вирішується за допомогою анотацій, але вони необов’язкові, не завжди перевіряються і т. Д. Тому вони не збережуть вас на межі з кодом сторонньої сторони.
Sarge Borsch

2
@SargeBorsch Я згоден з цим :-) Моє заперечення проти мови без стандартного способу виразити "будь ласка, ігноруйте це значення". nullдобре працює для цього (штрафу = просто), але стандартизовані необов'язкові значення із суцільною семантикою, безумовно, є ще одним точним варіантом (штраф = безпечно).
cmaster

2

Ще один аспект:

Java - статична мова з досить суворими можливостями. Це означає, що багато речей, які були б цілком відкритими або динамічними іншими мовами (c / f Ruby, Lisp тощо), суворо визначені.

Це загальне дизайнерське рішення. На "чому" важко відповісти (ну, адже дизайнери мови вважали, що це буде добре!). "Що для" досить зрозуміло: він дозволяє компілятору виявити безліч помилок, що, як правило, є досить хорошою особливістю для будь-якої мови. По-друге, досить легко міркувати про мову. Наприклад, відносно легко створити формальну коректність, що підтверджується (підмножинами) мови Java; для порівняння, це було б практично неможливо в такій динамічній мові, як Ruby et al.

Це мислення пронизує мову, наприклад, вимушене заяву про можливі винятки , що метод може кинути, окремий тип interfaceVS. , classщоб уникнути неоднозначного множинного спадкоємства, і так далі. Оскільки це (статична імперативна мова реального світу OOP з сильним упором на обробку помилок у часі компіляції), ці речі насправді є досить елегантними та потужними. Вони наближаються до теоретичних (наукових) мов, які цілеспрямовано створені лише для того, щоб вивчити деякі з цих питань, ніж будь-яка інша мова в реальному світі (на той час, пам'ятайте).

Тому. Суворий voidтип - це чітке повідомлення: цей метод нічого не повертає, періоду. Це те, що воно є. Заміна цього примусу завжди повертати щось призведе до або набагато більш динамічної поведінки (як, наприклад, у Ruby, де кожен def завжди має явне або неявне повернення), що було б погано для доказів та міркувань; або до масового набряку, використовуючи тут якийсь інший механізм.

(І NB: Рубі (наприклад) вирішує це по-різному, і його рішення все ще настільки ж прийнятне, як і у Java, тому що має абсолютно іншу філософію. Наприклад, він викидає провісність і розумність повністю у вікно, роблячи велику увагу на надзвичайно висока виразність мови.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.