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가 Nanny가 되려고하는 것입니까, 아니면 이것을 컴파일러 오류로 만드는 좋은 이유가 있습니까?


11
자바도 이것에 대해 완전히 일관성이 없습니다. 데드 코드를 유발하는 일부 제어 흐름의 경우 Java는 불평하지 않습니다. 다른 사람들에게는 그렇습니다. 죽은 코드를 결정하는 것은 계산할 수없는 문제입니다. Java가 끝낼 수없는 것을 시작하기로 결정한 이유를 모르겠습니다.
Paul Draper 2013 년

답변:


63

도달 할 수없는 코드는 컴파일러에게 의미가 없기 때문입니다. 사람들에게 의미있는 코드를 만드는 것이 컴파일러에게 의미있는 코드를 만드는 것보다 가장 중요하고 어렵지만 컴파일러는 코드의 필수 소비자입니다. 자바 설계자들은 컴파일러에게 의미가없는 코드는 오류라는 관점을 가지고 있습니다. 접근 할 수없는 코드가 있으면 수정해야 할 실수를 저질렀다는 입장입니다.

여기에 비슷한 질문이 있습니다. 연결할 수없는 코드 : 오류 또는 경고? 저자는 "개인적으로는 이것이 오류라고 생각합니다. 프로그래머가 코드를 작성하는 경우 항상 일부 시나리오에서 실제로 실행할 의도가 있어야합니다."라고 말합니다. 분명히 Java의 언어 디자이너는 동의합니다.

도달 할 수없는 코드가 컴파일을 방해해야하는지 여부는 합의가 이루어지지 않는 문제입니다. 그러나 이것이 자바 디자이너가 한 이유입니다.


댓글에있는 많은 사람들은 자바가 컴파일을 방해하지 않는 도달 할 수없는 코드 클래스가 많다고 지적합니다. Gödel의 결과를 올바르게 이해하면 어떤 컴파일러도 도달 할 수없는 코드의 모든 클래스를 포착 할 수 없습니다.

단위 테스트는 모든 단일 버그를 포착 할 수 없습니다. 우리는 이것을 그들의 가치에 대한 논쟁으로 사용하지 않습니다. 마찬가지로 컴파일러는 문제가있는 모든 코드를 잡을 수는 없지만 가능한 경우 잘못된 코드의 컴파일을 방지하는 것이 여전히 중요합니다.

Java 언어 디자이너는 연결할 수없는 코드를 오류로 간주합니다. 따라서 가능한 경우 컴파일을 방지하는 것이 합리적입니다.


(당신이 반대하기 전에 : 질문은 Java에 도달 할 수없는 명령문 컴파일러 오류가 있어야하는지 여부가 아닙니다. 문제는 Java에 도달 할 수없는 명령문 컴파일러 오류가있는 이유입니다. Java가 잘못된 설계 결정을 내렸다고 생각한다고해서 나를 반대하지 마십시오.)


22
분명히 자바의 언어 디자이너는 " 자바의 언어 디자이너 "가 인간이고 실수하기 쉽습니다. 개인적으로 나는 당신이 언급 한 토론의 다른 쪽이 훨씬 더 강력한 주장을 가지고 있다고 생각합니다.
Nikita Rybak

6
@Gabe : 무해하지 않기 때문에 거의 확실하게 실수입니다. 코드를 잘못된 위치에 넣었거나 일부에 도달 할 수없는 방식으로 성명서를 작성했다는 오해를 받았습니다. 이를 오류로 만들면 잘못된 코드가 작성되는 것을 방지 할 수 있으며, 다른 개발자가 읽을 수 있도록 코드의 도달 할 수없는 위치에있는 내용 (연결할 수없는 코드의 유일한 대상)을 원하면 주석을 대신 사용하십시오.
SamStephens

10
SamStephens : 호출되지 않은 메서드와 사용되지 않는 변수가 본질적으로 도달 할 수없는 코드의 모든 형태를 확인합니다. 일부 양식은 허용하고 다른 양식은 허용하지 않는 이유는 무엇입니까? 특히 이러한 유용한 디버깅 메커니즘을 허용하지 않는 이유는 무엇입니까?
Gabe

5
댓글도 도달 할 수 없습니까? 내 생각에 도달 할 수없는 코드는 실제로 주석의 한 형태입니다 (이전에 제가하려고했던 것 등). 그러나 "실제"댓글도 "접근 할 수"없다는 점을 고려하면 ... 아마도이 오류를 제기해야 할 수도 있습니다.)
Brady Moritz

10
문제는 그들이 (자바 언어 디자이너) 이것과 일치하지 않는다는 것입니다. 실제 흐름 분석이있는 경우 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"로 바꾸는 대신 가끔씩 미묘한 실수를 저질렀을 것입니다. 스위칭 로직을 한 번 코딩하고 필요하지 않으면 건드리지 않는 것이 좋습니다.
Robert

"코드를 읽는 사람은 누구나 의도적으로 수행 했음에 틀림 없다". 이것이 바로 제 생각에 문제입니다. 사람들은 추측해서는 안되며 이해해야합니다. 코드를 기억하는 것은 컴퓨터에 대한 지침이 아니라 사람과의 의사 소통입니다. 극도로 일시적인 것이 아니라면 내 의견으로는 구성을 사용해야합니다. 위에 표시 한 내용을 보면 if (true) return;논리를 건너 뛰는 이유가 아니라 if (BEHAVIOURDISABLED) return;의도를 전달합니다.
SamStephens

5
이 트릭은 디버깅에 정말 유용합니다. 실패한 부분을 찾으려고하는 메서드의 "나머지"를 제거하기 위해 종종 if (true) 반환을 추가합니다. 다른 방법이 있지만 이것은 깨끗하고 빠르지 만 확인해야 할 것은 아닙니다.
Bill K

18

유모입니다. 나는 .Net이 이것을 맞았다 고 느낍니다. 도달 할 수없는 코드에 대한 경고를 일으키지 만 오류는 아닙니다. 경고를받는 것은 좋지만 컴파일을 막을 이유는 없습니다 (특히 코드를 우회하기 위해 리턴을 던지는 것이 좋은 디버깅 세션 동안).


1
Java는 이전에 설계되었으며 당시에는 각 바이트가 계산되었지만 플로피 디스크는 여전히 첨단 기술이었습니다.
irreputable

1
디버그 조기 리턴의 경우 조기 리턴 후 줄을 주석 처리하는 데 5 초가 더 걸립니다. 도달 할 수없는 코드를 오류로 만들어서 방지 한 버그를 위해 플레이 할 작은 가격.
SamStephens

6
또한 컴파일러가 도달 할 수없는 코드를 제외하기 쉽습니다. 개발자가 그렇게하도록 강요 할 이유가 없습니다.
Brady Moritz

2
@SamStephens 이미 거기에 댓글이 있다면 댓글이 쌓이지 않기 때문에 댓글로이 문제를 해결하는 것은 불필요한 고통입니다.
enrey 2013

@SamStephens 주석 처리 된 코드는 제대로 리팩터링되지 않습니다.
cowlinator 2014

14

나는 방금이 질문을 알아 차렸고 여기에 $ .02를 더하고 싶었습니다.

Java의 경우 이것은 실제로 옵션이 아닙니다. "연결할 수없는 코드"오류는 JVM 개발자가 개발자를 모든 것으로부터 보호하거나 특별히 경계해야한다고 생각한 것이 아니라 JVM 사양의 요구 사항에서 비롯됩니다.

Java 컴파일러와 JVM은 모두 현재 메서드에 할당 된 스택의 모든 항목에 대한 명확한 정보 인 "스택 맵"을 사용합니다. JVM 명령이 한 유형의 항목을 다른 유형으로 잘못 처리하지 않도록 스택의 모든 슬롯 유형을 알아야합니다. 이것은 숫자 값이 포인터로 사용되는 것을 방지하는 데 가장 중요합니다. Java 어셈블리를 사용하여 숫자를 푸시 / 저장 한 다음 객체 참조를 팝 /로드 할 수 있습니다. 그러나 JVM은 클래스 유효성 검사 중에이 코드를 거부합니다. 즉, 스택 맵을 만들고 일관성을 테스트 할 때입니다.

스택 맵을 확인하기 위해 VM은 메서드에있는 모든 코드 경로를 살펴보고 실행될 코드 경로에 관계없이 모든 명령어의 스택 데이터가 이전 코드가 푸시 한 내용과 일치하는지 확인해야합니다. / 스택에 저장됩니다. 따라서 다음과 같은 간단한 경우 :

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

3 행에서 JVM은 'if'의 두 분기가 Object와 호환되는 항목 (로컬 var # 0 일뿐)에만 저장되었는지 확인합니다 (3 행 이상의 코드가 로컬 var # 0을 처리하는 방식이기 때문에) ).

컴파일러가 도달 할 수없는 코드에 도달하면 해당 지점에서 스택이 어떤 상태인지 알 수 없으므로 상태를 확인할 수 없습니다. 그 시점에서 더 이상 코드를 컴파일 할 수 없습니다. 지역 변수도 추적 할 수 없기 때문에 클래스 파일에이 모호함을 남기는 대신 치명적인 오류가 발생합니다.

물론 이와 같은 간단한 조건 if (1<2)은 속일 수 있지만 실제로는 속이지는 않습니다. 코드로 이어질 수있는 잠재적 분기를 제공하고 적어도 컴파일러와 VM 모두에서 스택 항목을 사용할 수있는 방법을 결정할 수 있습니다. 의 위에.

추신 :이 경우 .NET이 무엇을하는지 모르겠지만 컴파일도 실패 할 것이라고 생각합니다. 이것은 일반적으로 모든 기계 코드 컴파일러 (C, C ++, Obj-C 등)에서 문제가되지 않습니다.


Java의 데드 코드 경고 시스템은 얼마나 공격적입니까? 문법의 일부로 표현할 수 있습니까 (예 : { [flowingstatement;]* }=> flowingstatement, { [flowingstatement;]* stoppingstatement; }=> stoppingstatement, return=> stoppingstatement, if (condition) stoppingstatement; else stoppingstatement=> stoppingstatement;등)?
supercat 2013-08-05

나는 문법을 잘 못하지만 그렇다고 믿습니다. 그러나 "중지"는 로컬 일 수 있습니다 (예 : 루프 내부의 "break"문). 또한 메서드가 void 인 경우 메서드의 끝은 메서드 수준에 대한 암시 적 중지 문입니다. 'throw'는 명시적인 중지 문이기도합니다.
Pawel Veselov 2013 년

Java 표준 void blah() { try {return;} catch (RuntimeError ex) { } DoSomethingElse(); }try블록이 실제로 예외를 통해 종료 될 수있는 방법이 없다고 경고하거나 모든 종류의 검사되지 않은 예외가 모든 try블록 내에서 발생할 수 있다고 생각하는 것과 같은 것에 대해 무엇을 말 합니까?
supercat 2013-08-06

알아내는 가장 좋은 방법은 그것을 시도하고 컴파일하는 것입니다. 그러나 컴파일러는 심지어 빈 try / catch 문을 허용합니다. 그러나 모든 JVM 명령은 이론적으로 예외를 throw 할 수 있으므로 try 블록이 종료 될 수 있습니다. 그러나 이것이이 질문과 어떤 관련이 있는지 잘 모르겠습니다.
Pawel Veselov 2013 년

기본적으로 문제는 금지 된 "연결할 수없는"문이 실제로 표현식을 평가할 필요없이 구문 분석을 기반으로 연결할 수없는 것으로 식별 될 수있는 문인지 아니면 표준이 if (constantThatEqualsFive < 3) {doSomething;};.
supercat 2013-08-06

5

컴파일러의 목표 중 하나는 오류 클래스를 배제하는 것입니다. 도달 할 수없는 코드가 우연히 존재합니다. javac가 컴파일 타임에 해당 오류 클래스를 배제하는 것이 좋습니다.

잘못된 코드를 포착하는 모든 규칙에 대해 누군가는 자신이하는 일을 알고 있기 때문에 컴파일러가이를 받아들이기를 원할 것입니다. 이것이 컴파일러 검사의 불이익이며 균형을 올바르게 잡는 것은 언어 설계의 까다로운 점 중 하나입니다. 가장 엄격한 검사를하더라도 작성할 수있는 프로그램의 수는 무한하기 때문에 그렇게 나쁠 수는 없습니다.


5

이 컴파일러 오류는 좋은 것이라고 생각하지만 해결할 수있는 방법이 있습니다. 사실이라고 알고있는 조건을 사용하십시오.

public void myMethod(){

    someCodeHere();

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

    moreCodeHere();

}

컴파일러는 그것에 대해 불평 할만큼 똑똑하지 않습니다.


6
여기서 질문은 이유입니다. 연결할 수없는 코드는 컴파일러에게 의미가 없습니다. 따라서 유일한 청중은 개발자입니다. 댓글을 사용하세요.
SamStephens

3
그래서 나는 자바 사람들이 그들의 컴파일을 계획 한 방식에 동의하기 때문에 반대표를 얻습니까? Jeez ...
Sean Patrick Floyd

1
실제 질문에 답하지 않으면 반대표를받습니다. SamStephens의 의견을 참조하십시오.
BoltClock

2
javac 1<2는 이것이 사실임을 확실히 추론 할 수 있으며, 나머지 메소드는 바이트 코드에 포함될 필요가 없습니다. "도달 할 수없는 문"의 규칙은 명확하고 고정되어 있어야하며 컴파일러의 스마트 함과 무관해야합니다.
평판이 좋지 않음

1
@Sam은 그 태도로이 사이트의 베스트 답변의 50 %를 비추천해야 할 것입니다. 모든 IT 컨설팅 상황에서와 마찬가지로 SO에 대한 질문에 대답하는 데있어 중요한 부분은 주어진 질문에서 실제 질문 (또는 관련 부차 질문)을 식별하는 것입니다.
Sean Patrick Floyd

0

필요한 작업을 수행 할 수있는 한 컴파일러가 더 엄격할수록 더 좋다고 불평하는 것은 확실히 좋은 일입니다. 일반적으로 지불해야 할 적은 비용은 코드를 주석 처리하는 것이며, 코드를 컴파일하면 작동한다는 이점이 있습니다. 일반적인 예는 테스트 / 디버깅이 메인 테스트이고 짧은 테스트라는 것을 깨닫기 전까지 사람들이 비명을 지르는 Haskell입니다. 개인적으로 Java에서는 (사실 의도적으로)주의를 기울이지 않는 동안 거의 디버깅을하지 않습니다.


0

허용하는 이유 if (aBooleanVariable) return; someMoreCode;가 플래그를 허용 if (true) return; someMoreCode;하는 것이라면 컴파일러 true가 플래그 (변수가 아님)가 아닌 것을 '알고'있기 때문에 컴파일 시간 오류를 생성하지 않는 사실은 CodeNotReachable 예외 생성 정책에서 불일치처럼 보입니다 .

흥미로운 두 가지 다른 방법이 있지만 메서드 코드의 일부를 끄는 데는 적용되지 않습니다 if (true) return.

이제 말하지 않고 jvm 인수에 추가 if (true) return;하고 싶을 수도 있습니다 . 좋은 점은 이것이 약간의 세분성을 허용하고 jvm 호출에 추가 매개 변수를 추가해야한다는 것입니다. 따라서 코드에서 DEBUG 플래그를 설정할 필요가 없지만 런타임시 추가 된 인수를 통해 대상이 그렇지 않을 때 유용합니다. 개발자 컴퓨터와 바이트 코드를 다시 컴파일하고 전송하는 데 시간이 걸립니다.assert false-ea OR -ea package OR -ea className

System.exit(0)방법 도 있지만 JSP의 Java에 넣으면 서버가 종료됩니다.

Java가 설계에 의해 '유모'언어 인 것 외에도 더 많은 제어를 위해 C / C ++와 같은 네이티브 언어를 사용하고 싶습니다.


A로부터 뭔가 변경 static finalA를 정적 초기화 블록에 설정되는 변수 static final주요 변경해서는 안 선언에 설정되어있는 변수. 클래스가 처음 작성되었을 때 때때로 무언가를 할 수 없었을 수도 있지만 (그리고 그것이 할 수 있을지 여부를 런타임까지 알 수 없었던) 클래스의 이후 버전이 가능했을 수도 있다는 것은 전적으로 그럴듯합니다. 항상 그 행동을하십시오. myClass.canFoo상수로 변경하면 if (myClass.canFoo) myClass.Foo(); else doSomethingElse();더 효율적으로 만들 수 있습니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.