Чому Java має помилку компілятора "недосяжний вираз"?


83

Я часто виявляю, що під час налагодження програми зручно (хоча, можливо, погана практика) вставляти оператор return у блок коду. Я можу спробувати щось подібне на Java ....

class Test {
        public static void main(String args[]) {
                System.out.println("hello world");
                return;
                System.out.println("i think this line might cause a problem");
        }
}

звичайно, це призведе до помилки компілятора.

Test.java:7: недосяжне твердження

Я міг зрозуміти, чому попередження може бути виправданим, оскільки невикористаний код - це погана практика. Але я не розумію, чому це має спричинити помилку.

Це просто Java, яка намагається бути Нянею, чи є вагома причина зробити помилку компілятора?


11
Java також не повністю узгоджується з цим. Для деяких потоків управління, які викликають мертвий код, але Java не скаржиться. Для інших це так. Визначити, який код мертвий, є незрозумілою проблемою. Не знаю, чому Java вирішила розпочати щось, чого не могла закінчити.
Пол Дрейпер,

Відповіді:


63

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

Тут є подібне запитання: Недоступний код: помилка чи попередження? , в якому автор каже: "Особисто я впевнений, що це повинна бути помилка: якщо програміст пише фрагмент коду, він завжди повинен мати намір насправді запустити його за певним сценарієм". Очевидно, що мовні дизайнери Java погоджуються.

Чи повинен недоступний код перешкоджати компіляції - це питання, щодо якого ніколи не буде консенсусу. Але саме тому це зробили дизайнери Java.


Ряд людей у ​​коментарях зазначають, що існує безліч класів недосяжного коду, який Java не заважає компілювати. Якщо я правильно розумію наслідки Gödel, жоден компілятор не може схопити всі класи недосяжного коду.

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

Дизайнери мови Java вважають недосяжний код помилкою. Тому запобігання його компіляції, коли це можливо, є розумним.


(Перш ніж ви проголосуєте проти: питання полягає не в тому, чи повинна Java мати помилку недосяжного компілятора виразів . Питання полягає в тому, чому в Java є недосяжна помилка компілятора виразів. Не голосуйте проти мене лише тому, що ви вважаєте, що Java прийняла неправильне дизайнерське рішення.)


22
Очевидно, що мовні дизайнери Java погоджуються: "Мовні дизайнери Java" - це люди і теж схильні до помилок. Особисто я вважаю, що інша сторона дискусії, на яку ви посилаєтесь, має набагато вагоміші аргументи.
Микита Рибак

6
@Gabe: Оскільки це не нешкідливо - це майже напевно помилка. Або ви поставили свій код не в тому місці, або неправильно зрозуміли, що написали свою заяву таким чином, що до деяких з них неможливо дістатись. Виникнення цієї помилки запобігає написанню неправильного коду, і якщо ви хочете, щоб щось у недоступному місці вашого коду читали інші розробники (єдина аудиторія для недосяжного коду), використовуйте замість цього коментар.
SamStephens

10
SamStephens: бачачи, як не викликані методи та невикористані змінні є, по суті, усіма формами недосяжного коду. Чому дозволяються одні форми, а інші не? Зокрема, чому забороняти такий корисний механізм налагодження?
Gabe

5
Чи не є коментарі також недосяжними? На мою думку, недосяжний код насправді є формою коментарів (це те, що я намагався зробити раніше тощо). Але зважаючи на те, що "реальні" коментарі теж не "доступні" ... можливо, вони також повинні підняти цю помилку;)
Брейді Моріц

10
Проблема в тому, що вони (дизайнери мови Java) не суперечать цьому. Якщо існує фактичний аналіз потоку, то тривіально усвідомлювати, що і те, return; System.out.println();і інше if(true){ return; } System.out.println();гарантовано матимуть однакову поведінку, але одне - помилка компіляції, а інше - ні. Отже, коли я налагоджую і використовую цей метод для тимчасового «коментування» коду, я так це роблю. Проблема з коментуванням коду полягає в тому, що вам потрібно знайти відповідне місце, щоб зупинити ваш коментар, що може зайняти більше часу, ніж return;швидке введення .
LadyCailin

47

Немає остаточної причини, через яку недопустимі недоступні твердження; інші мови дозволяють їх без проблем. Для ваших конкретних потреб це звичайний фокус:

if (true) return;

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

Java має невелику підтримку "умовної компіляції"

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

if (false) { x=3; }

не призводить до помилки під час компіляції. Компілятор, що оптимізує, може усвідомити, що твердження x = 3; ніколи не буде виконано, і він може пропустити код для цього виразу із сформованого файлу класу, але вираз x = 3; не розглядається як "недосяжний" у зазначеному тут технічному розумінні.

Обґрунтування цього різного поводження полягає у наданні можливість програмістам визначати "позначення змінних", таких як:

static final boolean DEBUG = false;

а потім напишіть код, такий як:

if (DEBUG) { x=3; }

Ідея полягає в тому, що має бути можливість змінити значення DEBUG з false на true або з true на false, а потім правильно скомпілювати код без будь-яких інших змін у тексті програми.


2
Тим не менше не розумієш, чому ти турбуєшся трюком, який ти показуєш. Недосяжний код для компілятора безглуздий. Тож єдина аудиторія для нього - це розробники. Використовуйте коментар. Хоча я думаю, якщо ви тимчасово додаєте повернення, використання вашого обхідного способу може бути дробово швидшим, ніж просто введення повернення та коментування наступного коду.
SamStephens

8
@SamStephens Але кожного разу, коли ви хотіли перейти, вам доведеться пам'ятати всі місця, де вам доводиться коментувати код і де вам потрібно робити навпаки. Вам доведеться прочитати весь файл, змінивши вихідний код, мабуть, час від часу роблячи якісь тонкі помилки, замість того, щоб просто замінити "true" на "false" в одному місці. Я думаю, що краще один раз закодувати логіку перемикання і не чіпати її, якщо це не потрібно.
Роберт,

"кожен, хто прочитає код, здогадається, що це, мабуть, було зроблено навмисно". На мій погляд, це саме проблема, люди не повинні гадати, вони повинні розуміти. Запам'ятовування коду - це спілкування з людьми, а не просто інструкції для комп'ютерів. Якщо це щось інше, ніж надзвичайно тимчасове, ви, на мій погляд, повинні використовувати конфігурацію. Дивлячись на те, що ви показуєте вище, if (true) return;не вказує, ЧОМУ ви пропускаєте логіку, тоді як if (BEHAVIOURDISABLED) return;повідомляє про намір.
SamStephens

5
Цей трюк дійсно корисний для налагодження. Я часто додаю повернення if (true), щоб усунути "решту" методу, намагаючись з'ясувати, де він не працює. Є й інші шляхи, але це чисто і швидко - але це не те, що ви коли-небудь повинні реєструвати.
Bill K,

18

Це Няня. Я відчуваю,. Net зрозумів це правильно - воно видає попередження про недосяжний код, але не помилку. Добре бути попередженим про це, але я не бачу підстав для запобігання компіляції (особливо під час налагоджувальних сеансів, де приємно кинути повернення, щоб обійти якийсь код).


1
java була розроблена раніше, кожен байт нараховувався на той момент, дискети все ще були високотехнологічними.
незаперечний

1
Для налагодження дострокового повернення потрібно ще п’ять секунд, щоб прокоментувати рядки після вашого дострокового повернення. Невелика ціна, яку можна відіграти за помилки, запобігаючи тим, що робить недосяжний код помилкою.
SamStephens

6
компілятору також легко виключити недосяжний код. немає причин змушувати розробника це робити.
Brady Moritz

2
@SamStephens ні, якщо у вас там вже є коментарі, оскільки коментарі не складаються, тому зайвий біль обходити це коментарями.
enrey

@SamStephens Коментований код погано переробляє.
cowlinator

14

Я тільки що помітив це запитання і хотів додати до цього свої $ .02.

У випадку з Java це насправді не є варіантом. Помилка "недосяжний код" походить не від того, що розробники JVM думали захищати розробників від чого завгодно або бути надмірно пильними, а від вимог специфікації JVM.

І Java-компілятор, і JVM використовують те, що називається "картами стеків" - певну інформацію про всі елементи в стеку, як це виділено для поточного методу. Тип кожного слота стека повинен бути відомим, щоб інструкція JVM не поводилася з елементами одного типу для іншого типу. Це в основному важливо для запобігання тому, щоб числове значення ніколи не використовувалось як вказівник. За допомогою збірки Java можна спробувати натиснути / зберегти номер, але потім висунути / завантажити посилання на об’єкт. Однак JVM відхилить цей код під час перевірки класу, тобто коли карти стеків створюються та перевіряються на узгодженість.

Щоб перевірити карти стеків, віртуальна машина повинна пройти всі шляхи коду, які існують у методі, і переконатися, що незалежно від того, який шлях коду коли-небудь буде виконаний, дані стеку для кожної інструкції узгоджуються з тим, що натискав будь-який попередній код / зберігається в стосі. Отже, у простому випадку:

Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);

у рядку 3 JVM перевірить, що обидві гілки 'if' зберігали лише в (що є лише локальним змінним # 0) щось, що сумісне з Object (оскільки саме так код з рядка 3 і далі буде обробляти локальний var # 0 ).

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

Звичайно, така проста умова if (1<2)обдурить, але це насправді не обдурює - вона дає їй потенційну гілку, яка може призвести до коду, і принаймні як компілятор, так і віртуальна машина можуть визначити, як звідти можна використовувати елементи стека на.

PS Я не знаю, що робить .NET у цьому випадку, але я вважаю, що компіляція також не вдасться. Зазвичай це не буде проблемою для будь-яких компіляторів машинного коду (C, C ++, Obj-C тощо)


Наскільки агресивна система попередження про мертвий код Java? Чи можна це виразити як частину граматики (наприклад, { [flowingstatement;]* }=> flowingstatement, { [flowingstatement;]* stoppingstatement; }=> stoppingstatement, return=>stoppingstatement , if (condition) stoppingstatement; else stoppingstatement=> stoppingstatement;тощо?
supercat

Мені не підходить граматика, але я так вважаю, так. Однак "зупинка" може бути локальною, наприклад, оператор "break" у циклі. Крім того, кінець методу є неявним оператором зупинки для рівня методу, якщо метод недійсний. 'throw' також буде явним припиненням.
Павел Веселов

Що б сказав стандарт Java про щось подібне void blah() { try {return;} catch (RuntimeError ex) { } DoSomethingElse(); } Чи б Java squawk про те, що жодним чином tryблок не міг фактично вийти через виняток, або він міг би зрозуміти, що будь-який неперевірений виняток може статися в будь-якому tryблоці?
supercat

Найкращий спосіб це дізнатись - спробувати скласти це. Але компілятор дозволяє навіть порожні оператори try / catch. Однак будь-яка інструкція JVM теоретично може створити виняток, тому try block може вийти з цього. Але я не впевнений, як це стосується цього питання.
Павел Веселов

По суті, питання полягає в тому, чи єдиними забороненими "недосяжними" твердженнями є ті, які можна визначити як недосяжні на основі синтаксичного аналізу, без необхідності фактично оцінювати будь-які вирази, чи може стандарт забороняти недосяжні твердження на зразок if (constantThatEqualsFive < 3) {doSomething;};.
supercat

5

Однією з цілей компіляторів є виключення класів помилок. Якийсь недосяжний код є випадково, приємно, що javac виключає цей клас помилок під час компіляції.

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


5

Хоча я вважаю, що ця помилка компілятора - хороша річ, є спосіб, як її обійти. Використовуйте умову, яка, на вашу думку, буде істинною:

public void myMethod(){

    someCodeHere();

    if(1 < 2) return; // compiler isn't smart enough to complain about this

    moreCodeHere();

}

Компілятор недостатньо розумний, щоб скаржитися на це.


6
Тут виникає питання чому? Недосяжний код для компілятора безглуздий. Тож єдина аудиторія для нього - це розробники. Використовуйте коментар.
SamStephens

3
Отже, я отримую прихильників, тому що я погоджуюсь з тим, як хлопці Java розробляли компіляцію? Господи ...
Шон Патрік Флойд,

1
Ви отримуєте позитивні голоси за те, що не відповіли на фактичне питання. Див. Коментар SamStephens.
BoltClock

2
javac однозначно може зробити висновок, що 1<2це правда, а решту методу не потрібно включати в байт-код. правила "недосяжних тверджень" повинні бути чіткими, фіксованими та незалежними від розумності компіляторів.
беззаперечний

1
@Sam, маючи таке ставлення, вам доведеться проголосувати за 50% найкращих відповідей цього сайту (і я не кажу, що моя відповідь серед них). Велика частина відповідей на запитання щодо SO, як і в будь-якій ситуації з ІТ-консалтингом, полягає у визначенні справжнього питання (або відповідних побічних питань) із даного питання.
Шон Патрік Флойд,

0

Це, звичайно, добре скаржитися, чим жорсткіший компілятор, тим краще, наскільки це дозволяє вам робити те, що вам потрібно. Зазвичай невелика ціна - це коментування коду, виграш полягає в тому, що при компіляції ваш код працює. Загальним прикладом є Haskell, про який люди кричать, поки не усвідомлюють, що їх тест / налагодження - це лише основний і короткий тест. Я особисто на Java майже не виконую налагодження, будучи (насправді навмисно) не уважним.


0

Якщо причина дозволу if (aBooleanVariable) return; someMoreCode;- дозволити прапори, то той факт, що if (true) return; someMoreCode;не генерує помилку часу компіляції, здається невідповідністю політиці генерації винятку CodeNotReachable, оскільки компілятор "знає"true це не прапор (не змінна).

Ще два способи, які можуть бути цікавими, але не стосуються вимкнення частини коду методу, а також if (true) return:

Тепер, замість того, щоб говорити, if (true) return;ви можете сказати assert falseі додати -ea OR -ea package OR -ea classNameдо аргументів jvm. Хороший момент полягає в тому, що це забезпечує певну деталізацію і вимагає додавання додаткового параметра до виклику jvm, тому немає потреби встановлювати прапор DEBUG у коді, а додавати аргумент під час виконання, що корисно, коли ціль не є машина розробника та перекомпіляція та передача байт-коду вимагають часу.

Існує також System.exit(0)спосіб, але це може бути надмірним, якщо ви помістите його на Java у JSP, тоді сервер буде завершено.

Окрім того, що Java є побічною конструкцією мови "няні", я б скоріше використовував щось рідне, як C / C ++ для більшого контролю.


Зміна чогось із static finalзмінної, яка встановлена ​​в статичному блоці ініціалізації, на static finalзмінну, яка встановлена ​​в її оголошенні, не повинна бути суттєвою зміною. Цілком правдоподібно, що коли клас пишеться вперше, він, можливо, іноді не в змозі щось робити (і невідомо до часу виконання, чи зможе він це зробити чи ні), але пізніші версії класу могли б робити цю дію завжди. Перехід myClass.canFooна константу повинен зробити if (myClass.canFoo) myClass.Foo(); else doSomethingElse();більш ефективним, а не порушувати його.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.