Nice programing

Java가 배열에서 복사 된 경우 최종 변수에 대한 명시 적 캐스트가 필요한 이유는 무엇입니까?

nicepro 2020. 12. 25. 22:56
반응형

Java가 배열에서 복사 된 경우 최종 변수에 대한 명시 적 캐스트가 필요한 이유는 무엇입니까?


다음 코드로 시작합니다 ...

byte foo = 1;
byte fooFoo = foo + foo;

이 코드를 컴파일하려고하면 다음과 같은 오류가 발생합니다.

오류 : (5, 27) Java : 호환되지 않는 유형 : 정수에서 바이트로 손실 가능한 변환 가능

...하지만 foo최종적인 경우 ...

final byte foo = 1;
final byte fooFoo = foo + foo;

파일이 성공적으로 컴파일됩니다.

다음 코드로 이동 ...

final byte[] fooArray = new byte[1];
fooArray[0] = 1;

final byte foo = fooArray[0];
fooArray[0] = 127;

System.out.println("foo is: " + foo);

... 인쇄합니다

foo is: 1

... 괜찮습니다. 값은 최종 변수에 복사되며 더 이상 변경할 수 없습니다. 배열의 값을 가지고 놀아도 foo(예상대로 ...) 의 값이 변경되지 않습니다 .

다음 항목에 캐스트가 필요한 이유는 무엇입니까?

final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;

이 질문의 두 번째 예와 어떻게 다른가요? 컴파일러에서 다음 오류가 발생하는 이유는 무엇입니까?

오류 : (5, 27) Java : 호환되지 않는 유형 : 정수에서 바이트로 손실 가능한 변환 가능

어떻게 이런 일이 일어날 수 있습니까?


JLS ( §5.2 )에는 상수 표현식을 사용한 할당 변환에 대한 특수 규칙이 있습니다 .

또한, 식 일정한 표현 (경우 §15.28 형) byte, short, char, 또는 int:

  • 좁아 기본 변환 변수의 타입 인 경우 사용될 수있는 byte, short또는 char, 상기 식의 상수 값이 변수의 타입에서 표현할 수있다.

위의 링크를 따라 가면 상수 표현식 의 정의에서 다음과 같이 볼 수 있습니다 .

  • 기본 유형의 리터럴 및 유형의 리터럴 String
  • 덧셈 연산자 +-
  • 상수 변수를 참조하는 단순 이름 ( §6.5.6.1 ) ( §4.12.4 ).

위의 두 번째 링크를 따라 가면

기본 형식 또는 형식의 변수 String, 즉 final컴파일 타임 상수 식 ( §15.28 )으로 초기화 된 변수를 상수 변수 라고 합니다 .

그 다음 foo + foo단지에 할당 할 수있는 fooFoo경우에 fooA는 상수 변수 . 이를 귀하의 사례에 적용하려면 :

  • byte foo = 1; 상수 변수가 아니기 때문에 정의 하지 않습니다final .

  • final byte foo = 1; 않는 정의 상수 변수 가 있기 때문에, final및로 초기화 상수 식 (원시 리터럴).

  • final byte foo = fooArray[0]; 상수 표현식으로 초기화되지 않았기 때문에 상수 변수를 정의 하지 않습니다 .

여부를주의 fooFoo그 자체는 final중요하지 않습니다.


값 1은 바이트에 잘 맞습니다. 1 + 1도 마찬가지입니다. 변수가 최종일 때 컴파일러는 상수 접기를 수행 할 수 있습니다 . (즉, 컴파일러는 foo+ 연산을 수행 할 때 사용하지 않지만 "원시"1 값)

그러나 변수가 최종적이지 않으면 전환 및 프로모션에 대한 모든 흥미로운 규칙이 시작됩니다 ( 여기를 참조 하십시오 . 기본 전환 확대에 대한 섹션 5.12를 읽고 싶습니다).

두 번째 부분의 경우 : 배열을 최종화하면 해당 필드 변경할 수 있습니다. 그래서 다시; 지속적인 폴딩이 불가능합니다. "확대"작업이 다시 시작됩니다.


final바이트 코드에서 볼 수 있듯이 실제로 컴파일러와 함께 사용할 때 상수 접기에서 수행하는 작업입니다 .

    byte f = 1;
    // because compiler still use variable 'f', so `f + f` will 
    // be promoted to int, so we need cast
    byte ff = (byte) (f + f);
    final byte s = 3;
    // here compiler will directly compute the result and it know
    // 3 + 3 = 6 is a byte, so no need cast
    byte ss = s + s;
    //----------------------
    L0
    LINENUMBER 12 L0
    ICONST_1 // set variable to 1
    ISTORE 1 // store variable 'f'
    L1
    LINENUMBER 13 L1
    ILOAD 1 // use variable 'f'
    ILOAD 1
    IADD
    I2B        
    ISTORE 2 // store 'ff'
    L2

    LINENUMBER 14 L2
    ICONST_3 // set variable to 3
    ISTORE 3 // store 's'
    L3
    LINENUMBER 15 L3
    BIPUSH 6 // compiler just compute the result '6' and set directly
    ISTORE 4 // store 'ss'

마지막 바이트를 127로 변경하면 다음과 같이 불평합니다.

    final byte s = 127;
    byte ss = s + s;

이 경우 컴파일러는 결과를 계산하고 한계를 벗어난 것을 알고 있으므로 여전히 호환되지 않는다고 불평합니다.

더:

그리고 여기 문자열을 사용한 상수 접기에 대한 또 다른 질문이 있습니다.

참조 URL : https://stackoverflow.com/questions/43026241/why-does-java-require-an-explicit-cast-on-a-final-variable-if-it-was-copied-from

반응형