레이블이 지정된 휴식을 사용하는 것이 Java에서 좋은 방법입니까?


81

나는 2001 년의 오래된 코드를보고 있는데 다음과 같은 진술을 발견했다.

   else {
     do {
       int c = XMLDocumentFragmentScannerImpl.this.scanContent();
       if (c == 60) {
         XMLDocumentFragmentScannerImpl.this.fEntityScanner.scanChar();
         XMLDocumentFragmentScannerImpl.this.setScannerState(1);
         break label913;
       }

나는 전에 이것을 본 적이 없으며 여기에서 레이블이 지정된 휴식을 발견했습니다.

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

이것은 본질적으로 다음과 같이 작동하지 goto않습니까? 그것을 사용하는 것이 좋은 습관입니까? 그것은 나를 불안하게 만든다.


2
어디에 label913정의되어 있습니까?
Nikolay Kuznetsov

1
이 경우에는 꽤 큰 switch 문 끝에 약 200 줄이 걸립니다.
avgvstvs

계속하고 레이블 이름은 블록이 레이블로 표시된 코드 부분을 처리하는 코드를 만듭니다
user_CC

5
이 경우 문제는 switch 문의 크기입니다!
Cyrille Ka

8
이것은 생성 된 코드 (실제로는 파서)처럼 보입니다. 손으로 작성한 코드에는 사용되지 않는 생성 된 코드에 사용되는 많은 트릭이 있습니다 (주로 생성을 더 쉽게 만들기 때문입니다).
parsifal 2013

답변:


119

아니요, 제어 흐름의 다른 부분으로 "이동"할 수 없기 때문에 이동과는 다릅니다.

링크 한 페이지에서 :

break 문은 레이블이 지정된 문을 종료합니다. 제어 흐름을 레이블로 전달하지 않습니다. 제어 흐름은 레이블이 지정된 (종료 된) 명령문 바로 다음 명령문으로 전송됩니다.

이는 현재 실행중인 루프 만 중단 할 수 있음을 의미합니다.

이 예를 고려하십시오.

first:
for( int i = 0; i < 10; i++) {
  second:
  for(int j = 0; j < 5; j ++ )
  {
    break xxx;
  }
}

third:
for( int a = 0; a < 10; a++) {

}

당신은 대체 할 수 xxxfirstsecond모두 루프는 당신이 명중 할 때, 실행되고 있기 때문에, (외부 또는 내부 루프를 중단하기 위해) break문을하지만, 교체 xxx로하는 third컴파일되지 않습니다.


1
@user_CC 사실은 아니지만 여전히 제어 흐름에서 벗어날 수 없습니다. 레이블 된 continue는 레이블이 지정된 루프의 다음 반복을 시작하고 해당 루프가 이미 실행중인 동안에 만 작동합니다.
Thomas

3
그래서 break first;내가을 사용 하면 컴파일러가 즉시 라인으로 돌아가서 first:해당 루프를 다시 실행합니까?
Ungeheuer

15
더 @JohnnyCoder, break first; 루프가 라벨 (끝) first그것으로 계속, 즉 third. 반면에 continue first;의 현재 반복을 끝내고 그 와 함께 first중단 second하고 iin 의 다음 값을 계속합니다 first.
Thomas

1
@TheTechExpertGuy 음, 실제로는 아닙니다. 그것은 진술이 goto어느 정도 동일한 의미에서 비슷합니다 if-else. Goto는 루프 앞의 위치와 같이 "어디서나 이동"에서와 같이 훨씬 더 "유연"합니다 (사실 이전 언어가 루프를 구현하는 데 사용 된 방식입니다 : "조건이 충족되지 않으면 루프 본문의 시작으로 이동 (다시)"). . 나는 최근에 그들을 다루어야했고 나를 믿어야했다 goto. 레이블이 붙은 휴식은 그렇지 않은 반면 엉덩이에 진짜 고통이다.
Thomas

1
@TheTechExpertGuy 천만에요 :)-당신이 초보자이기 때문에 다시 한 번 조언을 드리고 싶습니다 goto. C ++에서 지원하더라도 전혀 사용하지 마십시오 . 나중에 많은 두통을 덜어 줄 문제를 해결하는 더 좋은 방법은 거의 항상 있습니다. 유혹이 올지도 ... 저항하라!
Thomas

16

goto레이블이 지정된 명령문 (일반적으로 루프 구성)의 끝까지 만 제어를 보내기 때문에 그렇게 끔찍하지는 않습니다 . 만드는 것은 gotounlikeable이 어디에 임의의 지점이라는 것이다 포함한 라벨은 당신이 집에서 재배 반복 동작을 할 수 있습니다 그래서, 방법 원에서 최대 높은 발견했다. Java의 레이블 분리 기능은 제어가 앞으로 만 진행되기 때문에 그런 종류의 광기를 허용하지 않습니다.

약 12 년 전에 한 번만 사용했습니다. 중첩 루프에서 벗어나야하는 상황에서 루프에서 더 복잡한 테스트를 위해 더 구조화 된 대안을 만들었을 것입니다. 나는 그것을 많이 사용하는 것을 권하지 않을 것이지만 자동적 인 잘못된 코드 냄새로 플래그를 지정하지는 않을 것입니다.


6
@Boann : 나는 goto가 모든 상황에서 완전히 악하다고 주장하는 것을 의미하지 않습니다. 그것은 포인터 산술, 연산자 오버로딩, 자바의 제작자가 문제를 해결할 가치가 없다고 결정한 명시 적 메모리 관리와 같은 것들 중 하나 일뿐입니다. 그리고 90 %가 넘는 것처럼 보였던 COBOL 프로그램을 읽은 후 Java에서 제외 된 것이 슬프지 않습니다.
Nathan Hughes

1
오, 당신은 @NathanHughes 처음 옳았습니다. 물론 모든 상황에서 그런 것은 아니지만 그렇습니다. 저는 그것을 놓치지 않습니다.
Perception

2
goto는 @Boann이라는 레이블보다 "유용"할 수 있지만 유지 관리 비용보다 훨씬 큽니다. 라벨은 마법의 지팡이가 아닙니다. 그것은 좋은 일입니다.
다비즈

9

항상 break새로운 방법 으로 대체 할 수 있습니다 .

두 목록의 공통 요소에 대한 코드 검사를 고려하십시오.

List list1, list2;

boolean foundCommonElement = false;
for (Object el : list1) {
    if (list2.contains(el)) {
        foundCommonElement = true;
        break;
    }
}

다음과 같이 다시 작성할 수 있습니다.

boolean haveCommonElement(List list1, List list2) {
    for (Object el : list1) {
        if (list2.contains(el)) {
            return true;
        }
    }
    return false;
}

물론 두 목록 사이의 공통 요소를 확인하려면 list1.retainAll(new HashSet<>(list2))메서드를 사용 O(n)하여 O(n)추가 메모리를 사용하거나 두 목록을 모두에서 정렬 O(n * log n)한 다음에서 공통 요소를 찾는 것이 O(n)좋습니다.


이것은 거의 항상 올바른 접근 방식입니다. goto너무 많은 장소를 보낼 수 있기 때문에 (잠재적으로) 해 롭습니다. 어떤 일이 이루어지고 있는지 전혀 분명하지 않습니다. 도우미 함수를 만드는 것은 당신이 당신이 그것을 할거야 일을하려고하고 어디서하는지에 관해서는 다음 사람에 명확한 경계를 제공합니다, 그리고 그것은 당신에게 기능의 조각을 이름을 수있는 기회를 제공합니다. 이 방법으로 더 명확합니다.
ethanbustad

@ethanbustad 그러나 이것은 라벨 break에 관한 것이지 않습니다 goto. 물론 여기에 표시된 리팩토링은 두 경우 모두 좋은 생각입니다. 그러나 레이블 break은 거의 조정되지 않으며 코드를 그대로 간직하는 경향 goto이 있습니다.
underscore_d

이 예는 끔찍합니다. 여기에있는 레이블이 쓸모없는 경우에는 break가 작동하지 않습니다.
Diracnote

8

이 답변의 나머지 부분을 읽기 전에 유해한 것으로 간주되는 문으로 이동을 읽어 보십시오 . 전체를 읽고 싶지 않다면 여기에 핵심 포인트가 있습니다.

go to 문을 자유롭게 사용하면 프로세스 진행 상황을 설명하는 의미있는 좌표 집합을 찾기가 매우 어려워지는 즉각적인 결과가 발생합니다.

또는 다시 goto말해 , 문제 는 프로그래머가 그 시점에서 프로그램 상태를 이해하지 않고도 프로그램이 코드 블록의 중간에 도착할 수 있다는 것입니다. 표준 블록 지향 구조는 상태 전이를 명확하게 묘사하도록 설계되었으며, 레이블 break은 프로그램을 특정 알려진 상태 (포함 레이블이있는 블록 외부)로 전환 하기위한 것입니다.

실제 명령형 프로그램에서 상태는 블록 경계에 의해 명확하게 묘사되지 않으므로 레이블 break이 좋은 아이디어 인지 여부는 의문 입니다. 블록이 블록 외부에서 보이는 상태를 변경하고 블록을 종료 할 여러 지점이있는 경우 레이블 break이있는은 기본 요소와 동일합니다 goto. 유일한 차이점은 불확정 상태의 블록 중간에 착륙 할 가능성이 아니라 불확정 상태로 새 블록을 시작한다는 것입니다.

따라서 일반적으로 레이블 break이 위험한 것으로 간주합니다 . 제 생각에는 블록을 둘러싸는 범위에 대한 제한된 액세스와 함께 함수로 변환해야한다는 신호입니다.

그러나이 예제 코드는 분명히 파서 생성기의 산물이었습니다 (OP는 이것이 Xerces 소스 코드라고 언급했습니다). 그리고 파서 생성기 (또는 일반적으로 코드 생성기)는 상태에 대한 완벽한 지식을 가지고 있고 인간이 그것을 이해할 필요가 없기 때문에 생성하는 코드에 자유를 가져다줍니다.


동의하지 않습니다. break여러 경우에 매우 유용합니다. 예를 들어, 여러 주문 된 소스에서 얻을 수있는 가치를 찾고있을 때. 당신은 하나씩 시도하고 있으며, 첫 번째가 발견되면 당신은 break. 보다 우아한 코드 if / else if / else if / else if입니다. (적어도 줄 수가 적습니다.) 모든 것을 메서드의 컨텍스트로 옮기는 것이 항상 실용적인 것은 아닙니다. 또 다른 경우는 일부 중첩 수준에서 조건부로 분리하는 경우입니다. 요컨대, 당신이를 좋아하지 않는다면 break, 당신은 return메소드의 끝을 제외하고는 다른 곳 을 좋아하지 않습니다 .
Ondra Žižka

문맥을 위해; "유해 고려로 이동하려면 문은"기록 된 때 같은 것들 dowhile존재하지 않는 모든 사람들이 사용하던 if(.. ) goto대신 사방. 언어 디자이너가 do및 같은 항목에 대한 지원을 추가하도록 권장하기위한 것 while입니다. 안타깝게도 악대 차가 시작되면 무지한 사람들이 break"한 번만 실행되는"루프와 같은 것들을 넣고 누가 무엇을 아는지에 대한 예외를 던지면서 goto더 깨끗하고 덜 해로운 모든 경우 에 대해 과대 광고로 성장했습니다 .
Brendan

1

이것은 goto흐름 제어를 뒤로 건너 뛰는 문장 과는 다릅니다. 레이블은 단순히 중단이 발생한 위치를 보여줍니다 (프로그래머). 또한 흐름 제어는 break.

그것을 사용하는 것에 관해서는 개인적으로 수천 줄의 코드를 작성하기 시작하면 그다지 중요하지 않기 때문에 개인적으로 그다지 유용하지 않다고 생각합니다. 그러나 다시 말하지만 사용 사례에 따라 다릅니다.


for(int i = 0; i < 1; i++) { do_something(); if(I_want_to_go_backwards) { i = 0; continue; } }.
Brendan

1

의도를 표현하는 더 깨끗한 방법은 적어도 내 생각에 루프를 포함하는 코드 조각을 별도의 메서드에 간단히 넣는 return것입니다.

예를 들어 다음을 변경하십시오.

someLabel:
for (int j = 0; j < 5; j++)
{
    // do something
    if ...
        break someLabel;
}

이것으로 :

private void Foo() {
    for (int j = 0; j < 5; j++)
    {
        // do something
        if ...
            return;
    }
}

이것은 또한 미래에 (또는 미래에 당신의 ) 코드로 작업 할 수있는 다른 언어에 능숙한 개발자들에게 더 관용적입니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.