tl; dr
들어 필드 , int b = b + 1
때문에 불법 b
에 불법 전방 참조입니다 b
. int b = this.b + 1
불만없이 컴파일되는를 작성하여 실제로이 문제를 해결할 수 있습니다 .
의 경우 지역 변수 , int d = d + 1
때문에 불법 d
사용하기 전에 초기화되지 않았습니다. 입니다 하지 항상 기본 초기화되어 필드의 경우.
컴파일을 시도하여 차이를 볼 수 있습니다.
int x = (x = 1) + x;
필드 선언 및 지역 변수 선언으로. 전자는 실패하지만 후자는 의미가 다르기 때문에 성공할 것입니다.
소개
우선, 필드 및 지역 변수 이니셜 라이저에 대한 규칙이 매우 다릅니다. 따라서이 답변은 두 부분으로 규칙을 다룰 것입니다.
이 테스트 프로그램을 다음과 같이 사용합니다.
public class test {
int a = a = 1;
int b = b + 1;
public static void Main(String[] args) {
int c = c = 1;
int d = d + 1;
}
}
의 선언 b
이 유효하지 않으며 오류와 함께 실패합니다 illegal forward reference
.
의 선언 d
이 유효하지 않으며 오류와 함께 실패합니다 variable d might not have been initialized
.
이러한 오류가 다르다는 사실은 오류의 원인도 다르다는 것을 암시해야합니다.
필드
Java의 필드 이니셜 라이저는 JLS §8.3.2 , 필드 초기화에 의해 관리됩니다 .
필드 의 범위 는 JLS §6.3 , 선언 범위에 정의되어 있습니다.
관련 규칙은 다음과 같습니다.
m
클래스 유형 C (§8.1.6)에서 선언되거나 상속 된 멤버 선언 의 범위는 중첩 된 유형 선언을 포함하여 C의 전체 본문입니다.
- 인스턴스 변수에 대한 초기화 표현식은 클래스에서 선언되거나 상속 된 모든 정적 변수의 간단한 이름을 사용할 수 있습니다. 선언이 나중에 텍스트로 발생하는 변수도 사용할 수 있습니다.
- 사용 후 선언이 텍스트로 나타나는 인스턴스 변수의 사용은 이러한 인스턴스 변수가 범위 내에 있더라도 때때로 제한됩니다. 인스턴스 변수에 대한 순방향 참조를 관리하는 정확한 규칙은 §8.3.2.3을 참조하십시오.
§8.3.2.3 내용 :
멤버 선언은 멤버가 클래스 또는 인터페이스 C의 인스턴스 (각각 정적) 필드이고 다음 조건이 모두 충족되는 경우에만 사용되기 전에 텍스트로 나타나야합니다.
- 사용은 C의 인스턴스 (각각 정적) 변수 이니셜 라이저 또는 C의 인스턴스 (각각 정적) 이니셜 라이저에서 발생합니다.
- 사용법은 과제의 왼쪽에 있지 않습니다.
- 사용법은 간단한 이름을 사용합니다.
- C는 사용법을 포함하는 가장 안쪽의 클래스 또는 인터페이스입니다.
특정 경우를 제외하고 선언되기 전에 실제로 필드를 참조 할 수 있습니다. 이러한 제한은 다음과 같은 코드를 방지하기위한 것입니다.
int j = i;
int i = j;
컴파일에서. 자바 스펙에 따르면 "위의 제한 사항은 컴파일 타임에 순환 또는 잘못된 형식의 초기화를 포착하도록 설계되었습니다."
이 규칙은 실제로 무엇으로 요약됩니까?
요컨대, 규칙은 기본적으로 (a) 참조가 이니셜 라이저에 있고 (b) 참조가 할당되지 않은 경우, (c) 참조가 해당 필드에 대한 참조보다 먼저 필드를 선언 해야 한다고 말합니다 . 간단한 이름 (같은 한정자 없음 this.
) 및 (d) 내부 클래스 내에서 액세스되지 않습니다. 따라서 네 가지 조건을 모두 충족하는 순방향 참조는 불법이지만 하나 이상의 조건에서 실패한 순방향 참조는 괜찮습니다.
int a = a = 1;
(b)를 위반하여 컴파일 됨 : 참조 a
가 할당되고 있으므로 의 완전한 선언 a
에 앞서 참조하는 것이 합법적 a
입니다.
int b = this.b + 1
또한 (c)를 위반하기 때문에 컴파일됩니다 : 참조 this.b
는 단순한 이름이 아닙니다 (로 한정됨 this.
). 이 이상한 구조는 this.b
값이 0 이기 때문에 여전히 완벽하게 잘 정의되어 있습니다.
따라서 기본적으로 이니셜 라이저 내의 필드 참조에 대한 제한으로 int a = a + 1
인해 성공적으로 컴파일되지 않습니다.
final 은 여전히 잘못된 포워드 참조 이므로 필드 선언 int b = (b = 1) + b
이 컴파일 되지 않습니다b
.
지역 변수
지역 변수 선언은 JLS §14.4 , 지역 변수 선언문 에 의해 관리됩니다 .
지역 변수 의 범위 는 JLS §6.3 , 선언 범위에 정의되어 있습니다 .
- 블록 (§14.4)에서 지역 변수 선언의 범위는 선언이 나타나는 나머지 블록으로, 자체 이니셜 라이저로 시작하고 지역 변수 선언문의 오른쪽에있는 추가 선언자를 포함합니다.
이니셜 라이저는 선언되는 변수의 범위 내에 있습니다. 그렇다면 왜 int d = d + 1;
컴파일 되지 않습니까?
그 이유는 명확한 할당 에 대한 Java의 규칙 ( JLS §16 ) 때문입니다. 명확한 할당은 기본적으로 지역 변수에 대한 모든 액세스에는 해당 변수에 대한 선행 할당이 있어야하며 Java 컴파일러는 루프와 분기를 검사하여 항상 사용 전에 할당이 발생 하는지 확인합니다 (이것이 명확한 할당에 전용 사양 섹션이있는 이유입니다. 그것에). 기본 규칙은 다음과 같습니다.
- 지역 변수 또는 빈 마지막 필드의 모든 액세스의 경우
x
, x
확실히 액세스하기 전에 할당, 또는 컴파일 타임 오류가 발생해야합니다.
에서 int d = d + 1;
에 대한 액세스 d
는 로컬 변수로 해결되지만 d
이전에 할당되지 않았으므로 d
컴파일러에서 오류를 발생시킵니다. 이어 int c = c = 1
, c = 1
먼저 일어나는 대입 c
한 후, 및 c
(1)이 할당의 결과로 초기화된다.
명확한 할당 규칙으로 인해 지역 변수 선언 int d = (d = 1) + d;
은 성공적으로 컴파일 됩니다 ( 필드 선언 과 달리int b = (b = 1) + b
) . d
최종 d
에 도달 할 때 확실히 할당 되기 때문 입니다 .
static
에서와 같이 클래스 범위 변수에 키워드를 추가static int x = x + 1;
하면 동일한 오류가 발생합니까? C #에서는 정적이든 비 정적이든 차이가 있기 때문입니다.