자바에서 goto 문에 대한 대안


84

Java 에서 goto 키워드에 대한 대체 기능은 무엇입니까 ?

Java에는 goto가 없기 때문에.


10
@harigm : Java에서 사용할 수있는 경우 작성할 코드 종류에 대한 예를 제공 할 수 있습니까 goto? 그 안에 더 중요한 문제가 있다고 생각합니다.
polygenelubricants 2010 년

3
@harigm : 두 번째 polygene, 프로그램에 goto가 필요한 이유를 포함하도록 질문을 업데이트 해 주시겠습니까?
모두

13
사람들은 항상 goto를 사용하지 않는 것에 대해 이야기하지만, 꽤 잘 알려져 있고 사용되는 정말 좋은 실제 사용 사례가 있다고 생각합니다. 즉, 함수에서 반환하기 전에 일부 코드를 실행해야합니다. 일반적으로 릴리스 잠금 또는 그렇지 않은 것이지만 제 경우에는 필수 정리를 수행 할 수 있도록 반환 직전에 휴식을 취할 수 있기를 바랍니다. 물론 여기 저기 고토가 산재 해있는 프로그램은 끔찍할 것입니다.하지만 메소드 본문으로 제한되는 경우 규칙을 따르는 한 그렇게 나쁘게 보이지는 않습니다 (기능의 끝으로 이동하고 백업하지 않음)
Matt 울프

7
당신은 좋은 회사에 있습니다. Linus Torvalds는 goto kerneltrap.org/node/553 을 열정적으로 옹호했습니다 . 의심 할 여지없이 Project Coin에서 가장 주목할만한 누락입니다.
Fletch 2011

7
@MattWolfe는 그 예제에서 작업을 시도하지 않습니까?
Andres Riofrio 2012 년

답변:


84

레이블이 지정된 BREAK 문을 사용할 수 있습니다 .

search:
    for (i = 0; i < arrayOfInts.length; i++) {
        for (j = 0; j < arrayOfInts[i].length; j++) {
            if (arrayOfInts[i][j] == searchfor) {
                foundIt = true;
                break search;
            }
        }
    }

그러나 올바르게 설계된 코드에서는 GOTO 기능이 필요하지 않습니다.


89
이것은 랩터로부터 당신을 구할 수 없습니다!
Bobby

25
올바르게 설계된 코드에 goto 기능이 필요하지 않은 이유는 무엇입니까?
rogermushroom

13
조립 만 사용할 수 있기 때문입니다. while 문, for 문, do-while, foreach, 함수 호출은 모두 제어되고 예측 가능한 방식으로 사용되는 GOTO 문입니다. 어셈블러 수준에서는 항상 GOTO이지만 순수한 GOTO가 제공하는 기능은 필요하지 않습니다. 함수 및 루프 / 제어 문에 비해 유일한 장점은 어디서나 코드 중간에 뛰어들 수 있다는 것입니다. . 그리고 그 '장점'은 그 자체로 '스파게티 코드'의 정의입니다.

1
@whatsthebeef 여기에 그가 이유를 설명 에츠 허르 데이크 스트라로 유명한 1968 편지입니다 고토 유해를 생각은 .
thomanski


42

gotoJava 의 개념 과 직접적으로 동등한 것은 없습니다 . 클래식으로 할 수있는 작업 중 일부 를 수행 할 수있는 몇 가지 구성이 있습니다 goto.

  • breakcontinue문은 루프 또는 스위치 문에서 블록 밖으로 점프 할 수 있습니다.
  • 레이블이 지정된 문 break <label>으로 임의의 복합 문에서 주어진 메서드 (또는 이니셜 라이저 블록) 내의 모든 수준으로 이동할 수 있습니다.
  • 루프 문에 레이블 continue <label>을 지정하면 내부 루프에서 외부 루프의 다음 반복을 계속할 수 있습니다 .
  • 예외를 던지고 잡으면 여러 수준의 메서드 호출에서 (효과적으로) 점프 할 수 있습니다. (그러나 예외는 상대적으로 비용이 많이 들고 "일반적인"제어 흐름 1 을 수행하는 나쁜 방법으로 간주됩니다 .)
  • 물론 return.

이러한 Java 구성 중 어느 것도 현재 명령문과 동일한 중첩 수준에있는 코드의 한 지점 또는 역방향 분기를 허용하지 않습니다. 그들은 모두 하나 이상의 중첩 (범위) 레벨을 continue뛰어 내리고 모두 (제외 ) 아래로 점프합니다. 이 제한은 이전 BASIC, FORTRAN 및 COBOL 코드 2에 내재 된 goto "스파게티 코드"증후군을 방지하는 데 도움이됩니다 .


1- 예외에서 가장 비용이 많이 드는 부분은 예외 개체와 스택 추적의 실제 생성입니다. 정말로 "정상"흐름 제어에 예외 처리를 사용해야하는 경우 예외 개체를 미리 할당 / 재사용하거나 fillInStackTrace()메서드 를 재정의하는 사용자 지정 예외 클래스를 만들 수 있습니다 . 단점은 예외의 printStackTrace()메서드가 유용한 정보를 제공하지 않는다는 것입니다.

2-스파게티 코드 신드롬은 사용 가능한 언어 구조의 사용을 제한 하는 구조적 프로그래밍 접근 방식을 생성했습니다. 이것은 BASIC , FortranCOBOL에 적용될 수 있지만주의와 규율이 필요했습니다. goto완전히 제거 하는 것이 실용적으로 더 나은 해결책이었습니다. 언어로 보관하면 항상 그것을 남용하는 광대가 있습니다.


while (...) {}for (...) {} 와 같은 반복 구조를 사용하면 명시 적으로 임의의 위치로 점프하지 않고도 코드 블록을 반복 할 수 있습니다. 또한 메서드 호출 myMethod ()를 사용하면 다른 곳에서 찾은 코드를 실행하고 완료되면 현재 블록으로 돌아갈 수 있습니다. 이들 모두는 goto가 허용하는 반환 실패의 일반적인 문제를 방지하면서 goto의 기능을 대체하는 데 사용할 수 있습니다.
Chris Nava

@Chris-사실이지만 GOTO를 지원하는 대부분의 언어는 Java 루프 구조에 더 가까운 유사성을 가지고 있습니다.
Stephen C

1
@Chris-클래식 FORTRAN에는 'for'루프가 있으며 클래식 COBOL에는 루프 구조도 있습니다 (세부 사항을 기억할 수는 없지만). 클래식 BASIC에만 명시적인 루프 구조가 없습니다 ...
Stephen C

31

그냥 재미를 위해, 여기에 자바에서 GOTO 구현입니다.

예:

   1 public class GotoDemo {
   2     public static void main(String[] args) {
   3         int i = 3;
   4         System.out.println(i);
   5         i = i - 1;
   6         if (i >= 0) {
   7             GotoFactory.getSharedInstance().getGoto().go(4);
   8         }
   9         
  10         try {
  11             System.out.print("Hell");
  12             if (Math.random() > 0) throw new Exception();            
  13             System.out.println("World!");
  14         } catch (Exception e) {
  15             System.out.print("o ");
  16             GotoFactory.getSharedInstance().getGoto().go(13);
  17         }
  18     }
  19 }

실행 :

$ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo           
   3
   2
   1
   0
   Hello World!

"사용하지 마십시오!"를 추가해야합니까?


1
나는 그 여분의 사용 습관
gmhk

2
버그 : Math.random()0을 반환 할 수 있습니다.> =이어야합니다.
poitroae 2012-08-20

네. 단지 바이트 코드 해커에 의해 구현 될 수있는 일이 ... 정말 자바 아니다
스티븐 C

줄 번호 대신 레이블을 지원하는 비슷한 것이 있습니까?
Behrouz.M

1
구현에 대한 링크가 끊어졌습니다.
LarsH

17

일부 주석가와 반대 투표자들은 이것이 goto 가 아니라고 주장하지만 , 아래 Java 문에서 생성 된 바이트 코드는 실제로 이러한 문이 goto 의미를 표현한다는 것을 실제로 암시합니다 .

특히 do {...} while(true);두 번째 예제의 루프는 루프 조건을 평가하지 않기 위해 Java 컴파일러에 의해 최적화됩니다.

앞으로 점프

label: {
  // do stuff
  if (check) break label;
  // do more stuff
}

바이트 코드에서 :

2  iload_1 [check]
3  ifeq 6          // Jumping forward
6  ..

뒤로 점프

label: do {
  // do stuff
  if (check) continue label;
  // do more stuff
  break label;
} while(true);

바이트 코드에서 :

 2  iload_1 [check]
 3  ifeq 9
 6  goto 2          // Jumping backward
 9  ..

do / while은 어떻게 뒤로 건너 뛰나요? 여전히 블록을 벗어납니다. 카운터 예 : label : do {System.out.println ( "Backward"); 계속 레이블;} while (false); 귀하의 예에서 while (true)이 뒤로 점프하는 원인이라고 생각합니다.
Ben Holland

@BenHolland : continue label점프 거꾸로
루카스 에더

나는 아직도 확신하지 못한다. continue 문은 for, while 또는 do-while 루프의 현재 반복을 건너 뜁니다. continue는 루프 본문의 끝으로 건너 뛴 다음 루프를 제어하는 ​​부울 표현식에 대해 평가됩니다. 동안은 계속이 아니라 뒤로 점프하는 것입니다. docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Ben Holland

@BenHolland : 기술적으로 당신 말이 맞습니다. 논리적 인 관점에서, 경우 // do stuff// do more stuff관심의 문이다 continue label "효과있다" (때문에에 점프 뒤쪽의를 while(true)물론 문). 나는이 같은 정확한 문제는 것을 인식하지 않았다하지만 ...
루카스 에더

2
@BenHolland : 답변을 업데이트했습니다. 은 while(true)(A) 내로 컴파일러에 의해 번역 goto바이트 동작. 으로 true일정의 리터럴, 컴파일러는이 최적화를 할 수있는 아무것도를 평가하지 않습니다. 그래서, 내 예를 정말 뒤로 점프 고토는 ...이다
루카스 에더

5

goto 문과 같은 것을 정말로 원한다면 항상 명명 된 블록으로 분리 할 수 ​​있습니다.

레이블로 분리하려면 블록 범위 내에 있어야합니다.

namedBlock: {
  if (j==2) {
    // this will take you to the label above
    break namedBlock;
  }
}

고토를 피해야하는 이유에 대해서는 설명하지 않겠습니다. 이미 그에 대한 답을 알고 있다고 가정합니다.


2
여기에 연결된 내 SO 질문에서 이것을 구현해 보았습니다 . 실제로는 "위의 레이블"로 이동하지 않습니다. 아마도 저자의 진술을 잘못 해석했을 수도 있지만 명확성을 위해 아래에있는 외부 블록 ( "레이블이있는"블록)의 끝으로 이동한다고 추가하겠습니다. 당신이 깨는 곳. 그러므로 당신은 위로 "파괴"할 수 없습니다. 적어도 그것은 나의 이해입니다.
NameSpace

4
public class TestLabel {

    enum Label{LABEL1, LABEL2, LABEL3, LABEL4}

    /**
     * @param args
     */
    public static void main(String[] args) {

        Label label = Label.LABEL1;

        while(true) {
            switch(label){
                case LABEL1:
                    print(label);

                case LABEL2:
                    print(label);
                    label = Label.LABEL4;
                    continue;

                case LABEL3:
                    print(label);
                    label = Label.LABEL1;
                    break;

                case LABEL4:
                    print(label);
                    label = Label.LABEL3;
                    continue;
            }
            break;
        }
    }

    public final static void print(Label label){
        System.out.println(label);
    }

이로 인해 StackOverFlowException이 발생하므로 충분히 오래 실행하면 이동해야합니다.
Kevin Parker

3
@Kevin : 이것이 어떻게 스택 오버플로로 이어질까요? 이 알고리즘에는 재귀가 없습니다 ...
루카스 에더

1
이것은 "goto"를 사용하지 않고 프로그램을 작성할 수 있다는 원래의 증거와 매우 유사합니다. 어떤 goto보다 10 배 더 나쁜 "label"변수를 가진 while 루프로 바꾸는 것입니다.
gnasher729 2014-06-12

3

StephenC는 다음과 같이 씁니다.

고전적인 goto로 할 수있는 일을 할 수있는 두 가지 구조가 있습니다.

하나 더...

Matt Wolfe는 다음과 같이 씁니다.

사람들은 항상 goto를 사용하지 않는 것에 대해 이야기하지만, 꽤 잘 알려져 있고 사용되는 정말 좋은 실제 사용 사례가 있다고 생각합니다. 즉, 함수에서 반환하기 전에 일부 코드를 실행해야합니다. 일반적으로 릴리스 잠금 또는 그렇지 않은 것이지만 제 경우에는 필수 정리를 수행 할 수 있도록 반환 직전에 휴식을 취할 수 있기를 바랍니다.

try {
    // do stuff
    return result;  // or break, etc.
}
finally {
    // clean up before actually returning, even though the order looks wrong.
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

finally 블록은 try 블록이 종료 될 때 항상 실행됩니다. 이렇게하면 예기치 않은 예외가 발생하더라도 finally 블록이 실행됩니다. 그러나 마지막으로 예외 처리 이상의 용도로 유용합니다. 프로그래머가 반환, 계속 또는 중단으로 인해 실수로 정리 코드를 우회하는 것을 방지 할 수 있습니다. 정리 코드를 finally 블록에 넣는 것은 예외가 예상되지 않는 경우에도 항상 좋은 방법입니다.

finally와 관련된 어리석은 인터뷰 질문은 다음과 같습니다. try {} 블록에서 돌아 왔지만 finally {}에도 반환이있는 경우 어떤 값이 반환됩니까?


2
나는 그것이 어리석은 인터뷰 질문이라는 것에 동의하지 않습니다. 숙련 된 자바 프로그래머 해야 이해하는 경우에만 발생 알고 을 넣어 returnA의 finally블록은 좋은 생각이다. (지식은 꼭 필요하지 않더라도, 나는이 ... 찾아 호기심을 가지고 있지 못한 프로그래머가 걱정 것)
스티븐 C

1

가장 쉬운 방법은 다음과 같습니다.

int label = 0;
loop:while(true) {
    switch(state) {
        case 0:
            // Some code
            state = 5;
            break;

        case 2:
            // Some code
            state = 4;
            break;
        ...
        default:
            break loop;
    }
}

0

아래 코드를 시도하십시오. 그것은 나를 위해 작동합니다.

for (int iTaksa = 1; iTaksa <=8; iTaksa++) { // 'Count 8 Loop is  8 Taksa

    strTaksaStringStar[iCountTaksa] = strTaksaStringCount[iTaksa];

    LabelEndTaksa_Exit : {
        if (iCountTaksa == 1) { //If count is 6 then next it's 2
            iCountTaksa = 2;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 2) { //If count is 2 then next it's 3
            iCountTaksa = 3;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 3) { //If count is 3 then next it's 4
            iCountTaksa = 4;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 4) { //If count is 4 then next it's 7
            iCountTaksa = 7;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 7) { //If count is 7 then next it's 5
            iCountTaksa = 5;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 5) { //If count is 5 then next it's 8
            iCountTaksa = 8;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 8) { //If count is 8 then next it's 6
            iCountTaksa = 6;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 6) { //If count is 6 then loop 1  as 1 2 3 4 7 5 8 6  --> 1
            iCountTaksa = 1;
            break  LabelEndTaksa_Exit;
        }
    }   //LabelEndTaksa_Exit : {

} // "for (int iTaksa = 1; iTaksa <=8; iTaksa++) {"

-1

goto의 대안으로 레이블이 지정된 휴식을 사용하십시오.


이 대답은, 항상 중복이었다
스티븐 C

-1

Java에는 goto코드가 구조화되지 않고 읽기가 명확하지 않기 때문에 . 그러나, 당신은 사용할 수 있습니다 breakcontinue같은 문명 의 형태로 고토 의 문제없이.


브레이크를 사용하여 앞으로 점프-

ahead: {
    System.out.println("Before break");
    break ahead;
    System.out.println("After Break"); // This won't execute
}
// After a line break ahead, the code flow starts from here, after the ahead block
System.out.println("After ahead");

출력 :

Before Break
After ahead

계속을 사용하여 뒤로 점프

before: {
    System.out.println("Continue");
    continue before;
}

라인 이 실행될 때마다 코드 흐름이에서 다시 시작 되므로 무한 루프 가 발생 합니다 .continue beforebefore


2
계속을 사용하여 뒤로 점프하는 예가 잘못되었습니다. 루프 본문 외부에서 "계속"을 사용할 수 없으며 컴파일 오류가 발생합니다. 그러나 루프 내부에서 계속하면 루프 조건부로 건너 뜁니다. 따라서 while (true) {continue;}와 같은 것은 무한 루프이지만 그 문제에 대해서는 while (true) {}도 마찬가지입니다.
Ben Holland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.