리턴 유형이없는 Java 메소드는 리턴 명령문없이 컴파일


228

질문 1:

다음 코드가 return 문없이 컴파일되는 이유는 무엇입니까?

public int a() {
    while(true);
}

통지 : 잠시 후에 return을 추가하면을 얻습니다 Unreachable Code Error.

질문 2 :

반면에 다음 코드는 왜 컴파일됩니까?

public int a() {
    while(0 == 0);
}

비록 다음과 같지 않지만.

public int a(int b) {
    while(b == b);
}

2
두 번째 질문의 후반부 덕분 에 stackoverflow.com/questions/16789832/… 의 사본이 아닙니다 .
TJ Crowder

답변:


274

질문 1:

다음 코드가 return 문없이 컴파일되는 이유는 무엇입니까?

public int a() 
{
    while(true);
}

이것은 JLS§8.4.7에 의해 다루어진다 :

메소드가 리턴 유형 (§8.4.5)을 갖도록 선언 된 경우 메소드 본문이 정상적으로 완료 될 수 있으면 (§14.1) 컴파일 타임 오류가 발생합니다.

다시 말해, 리턴 유형이있는 메소드는 값 리턴을 제공하는 리턴 명령문을 사용하여 리턴해야합니다. 이 방법은 "본체의 끝을 제거"할 수 없습니다. 메소드 본문의 리턴 문에 대한 정확한 규칙은 §14.17을 참조하십시오.

메소드가 리턴 유형을 가질 수 있지만 리턴 명령문을 포함하지 않을 수 있습니다. 다음은 하나의 예입니다.

class DizzyDean {
    int pitch() { throw new RuntimeException("90 mph?!"); }
}

컴파일러는 루프가 종료되지 않는다는 것을 알고 있기 때문에 ( true물론 항상 참임), 함수가 "정상적으로 복귀 할 수 없음"(본체의 끝을 떨어 뜨림) 할 수 없다는 것을 알고 있으므로 아무 것도 없어도 return됩니다.

질문 2 :

반면에 다음 코드는 왜 컴파일됩니까?

public int a() 
{
    while(0 == 0);
}

비록 다음과 같지 않지만.

public int a(int b)
{
    while(b == b);
}

0 == 0경우 컴파일러는 루프가 종료되지 않는다는 것을 알고 0 == 0있습니다 ( 항상 참임). 그러나 그것은 하지 않습니다 에 대한 알고 b == b.

왜 안돼?

컴파일러는 상수 표현식 (§15.28)을 이해 합니다. 인용 §15.2-표현의 형태 (이상하게도이 문장이 §15.28에 없기 때문에) :

일부 표현식에는 컴파일 타임에 결정할 수있는 값이 있습니다. 이들은 일정한 표현입니다 (§15.28).

귀하의 b == b예에서 관련된 변수가 있기 때문에 상수 표현식이 아니며 컴파일 타임에 결정되도록 지정되지 않았습니다. 우리는 (경우에 있지만 항상이 경우에는 사실이 될 것 것을 볼 수 b있었던이 doubleQBrute과 같이, 지적 , 우리가 쉽게 현혹 될 수 Double.NaN있는, 아니 ==그 자체 )하지만, JLS 상수 표현식이 컴파일시에 결정됩니다 만 지정 컴파일러가 상수가 아닌 표현식을 평가할 수 없습니다. bayou.io 는 다음과 같은 이유에 대해 좋은 지적 을했습니다. b == b명백하다 (er, non-NaN값), 그러나 어떻 a + b == b + a습니까? 아니면 (a + b) * 2 == a * 2 + b * 2? 상수로 선을 그리는 것이 좋습니다.

따라서 표현식을 "결정"하지 않기 때문에 컴파일러는 루프가 종료되지 않는다는 것을 알지 못하므로 메소드가 정상적으로 리턴 될 수 있다고 생각합니다 return. 따라서의 부족에 대해 불평합니다 return.


34

메소드 리턴 유형을 지정된 유형 의 값을 리턴하는 약속 이 아니라 지정된 유형 이 아닌 값을 리턴 하지 않는 약속으로 생각하면 흥미로울 수 있습니다 . 따라서 아무 것도 반환하지 않으면 약속을 어 기지 않으므로 다음 중 하나가 합법입니다.

  1. 영원히 반복 :

    X foo() {
        for (;;);
    }
  2. 영원히 되풀이 :

    X foo() {
        return foo();
    }
  3. 예외를 던지기 :

    X foo() {
        throw new Error();
    }

(재밌는 재귀를 발견했습니다. 컴파일러는 메소드가 유형의 값 X(무엇이든)을 리턴 할 것이라고 생각 하지만 생성 또는 생성 방법에 대한 아이디어가있는 코드가 없기 때문에 사실이 아닙니다. 를 조달하십시오 X.)


8

바이트 코드를 보면 반환되는 내용이 정의와 일치하지 않으면 컴파일 오류가 발생합니다.

예:

for(;;) 바이트 코드를 보여줍니다 :

L0
    LINENUMBER 6 L0
    FRAME SAME
    GOTO L0

리턴 바이트 코드가 없음에 유의하십시오.

이것은 결코 리턴을 누르지 않으므로 잘못된 유형을 리턴하지 않습니다.

비교를 위해 다음과 같은 방법이 있습니다.

public String getBar() { 
    return bar; 
}

다음 바이트 코드를 반환합니다.

public java.lang.String getBar();
    Code:
      0:   aload_0
      1:   getfield        #2; //Field bar:Ljava/lang/String;
      4:   areturn

"참조"를 의미하는 "return"에 유의하십시오.

이제 다음을 수행하면

public String getBar() { 
    return 1; 
}

다음 바이트 코드를 반환합니다.

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

이제 정의의 유형이 ireturn의 리턴 유형과 일치하지 않음을 알 수 있습니다. 이는 return int를 의미합니다.

따라서 실제로 결과는 메소드에 리턴 경로가 있으면 해당 경로가 리턴 유형과 일치해야한다는 것입니다. 그러나 바이트 경로에는 반환 경로가 전혀 생성되지 않아 규칙을 어 기지 않는 인스턴스가 있습니다.

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