Java 8에서 람다를 가지고 놀고 있는데 경고가 나왔습니다 local variables referenced from a lambda expression must be final or effectively final
. 익명 클래스에서 변수를 사용할 때 외부 클래스에서 최종 변수 여야하지만 여전히 final 과 효과적으로 final 의 차이점은 무엇 입니까?
Java 8에서 람다를 가지고 놀고 있는데 경고가 나왔습니다 local variables referenced from a lambda expression must be final or effectively final
. 익명 클래스에서 변수를 사용할 때 외부 클래스에서 최종 변수 여야하지만 여전히 final 과 효과적으로 final 의 차이점은 무엇 입니까?
답변:
... Java SE 8부터 로컬 클래스는 최종 또는 효과적으로 최종 블록의 로컬 변수 및 매개 변수에 액세스 할 수 있습니다. 초기화 후에 값이 변경되지 않는 변수 또는 매개 변수는 사실상 최종입니다.
예를 들어 변수 numberLength
가 final로 선언되지 않고 PhoneNumber
생성자 에 표시된 대 입문을 추가 한다고 가정 하십시오 .
public class OutterClass {
int numberLength; // <== not *final*
class PhoneNumber {
PhoneNumber(String phoneNumber) {
numberLength = 7; // <== assignment to numberLength
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
...
}
...
}
이 대 입문 때문에 변수 numberLength는 더 이상 최종적으로 유효하지 않습니다. 결과적으로 Java 컴파일러 는 내부 클래스 PhoneNumber가 numberLength 변수에 액세스하려고 시도하는 "내부 클래스에서 참조 된 로컬 변수는 최종적이거나 효과적으로 최종적이어야합니다"와 유사한 오류 메시지를 생성 합니다.
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
numberLength
의 로컬 변수가됩니다.
"효과적으로 최종"을 설명하는 가장 간단한 방법 final
은 변수 선언에 수정자를 추가하는 것을 상상하는 것 입니다. 이 변경으로 인해 프로그램이 컴파일 타임과 런타임 모두에서 동일한 방식으로 계속 작동하면 해당 변수는 사실상 최종입니다.
case k
있는 상수 표현식 을 요구하도록 지정합니다 ( "상수 변수는 기본 표현식 또는 상수 문자열로 초기화되는 유형 문자열의 최종 변수" JLS 4.12.4 ). 변하기 쉬운.
문서 에 따르면 :
초기화 후에 값이 변경되지 않는 변수 또는 매개 변수는 사실상 최종입니다.
기본적으로 컴파일러가 초기화 외부의 할당에 변수가 나타나지 않으면 변수는 효과적으로 final 로 간주됩니다 .
예를 들어 다음과 같은 클래스를 고려하십시오.
public class Foo {
public void baz(int bar) {
// While the next line is commented, bar is effectively final
// and while it is uncommented, the assignment means it is not
// effectively final.
// bar = 2;
}
}
bar
귀하의 예에서 지역 가변형이 아니라 필드입니다. 위와 같은 오류 메시지의 "효과적으로 최종"은 필드에 전혀 적용되지 않습니다.
bar
는 필드가 아닌 매개 변수입니다.
'Effectively final'은 'final'에 의해 추가 될 경우 컴파일러 오류를 발생시키지 않는 변수입니다.
'Brian Goetz'의 기사에서
비공식적으로, 로컬 변수는 초기 값이 변경되지 않으면 사실상 최종입니다. 즉, 최종 선언하면 컴파일 실패가 발생하지 않습니다.
아래의이 변수는 final 이므로 초기화되면 값을 변경할 수 없습니다. 우리가 시도하면 컴파일 오류가 발생합니다 ...
final int variable = 123;
그러나 이와 같은 변수를 만들면 값을 변경할 수 있습니다.
int variable = 123;
variable = 456;
그러나 Java 8 에서는 모든 변수가 기본적으로 최종 변수 입니다. 그러나 코드에 두 번째 줄이 있으면 최종적이지 않습니다 . 따라서 위 코드에서 두 번째 줄을 제거하면 변수가 "효과적으로 최종"입니다 .
int variable = 123;
그래서 .. 한 번만 할당 된 모든 변수는, "효과적으로 최종"입니다 .
람다식이 둘러싸는 공간에서 할당 된 로컬 변수를 사용하는 경우 중요한 제한이 있습니다. 람다 식은 값이 변경되지 않는 로컬 변수 만 사용할 수 있습니다. 이러한 제한을 " 변수 캡처 " 라고 합니다 . 변수가 아닌 람다 식 캡처 값 .
람다식이 사용할 수있는 지역 변수를 " 실제로 최종 "이라고합니다.
효과적으로 최종 변수는 처음 지정된 후에 값이 변경되지 않는 변수입니다. 이러한 변수를 final로 명시 적으로 선언 할 필요는 없지만 오류는 아닙니다.
예를 들어 보자. 우리는 지역 변수 i를 값 7로 초기화했다. 람다 식에서 우리는 i에 새로운 값을 할당하여 그 값을 변경하려고한다. 이로 인해 컴파일러 오류가 발생합니다. " 둘러싸는 범위에 정의 된 로컬 변수 i는 최종적이거나 효과적으로 최종적이어야합니다. "
@FunctionalInterface
interface IFuncInt {
int func(int num1, int num2);
public String toString();
}
public class LambdaVarDemo {
public static void main(String[] args){
int i = 7;
IFuncInt funcInt = (num1, num2) -> {
i = num1 + num2;
return i;
};
}
}
효과적인 최종 주제는 JLS 4.12.4에 설명되어 있으며 마지막 단락은 명확한 설명으로 구성됩니다.
변수가 효과적으로 최종 변수 인 경우 최종 수정자를 선언에 추가하면 컴파일 타임 오류가 발생하지 않습니다. 반대로, 최종 수정자가 제거되면 유효한 프로그램에서 final로 선언 된 로컬 변수 또는 매개 변수가 효과적으로 최종이됩니다.
final 은 키워드로 선언 한 변수입니다 ( final
예 :
final double pi = 3.14 ;
그것은 final
프로그램을 통해 남아 있습니다.
효과적으로 final : 현재 한 번만 값이 할당되거나 한 번만 업데이트되는 로컬 변수 또는 매개 변수. 프로그램 전체에 걸쳐 최종적으로 유효 하지 않을 수 있습니다 . 따라서 효과적으로 최종 변수는 최소한 하나 이상의 할당을 할당 / 업데이트 한 후 즉시 최종 속성을 잃을 수 있습니다. 예:
class EffectivelyFinal {
public static void main(String[] args) {
calculate(124,53);
}
public static void calculate( int operand1, int operand2){
int rem = 0; // operand1, operand2 and rem are effectively final here
rem = operand1%2 // rem lost its effectively final property here because it gets its second assignment
// operand1, operand2 are still effectively final here
class operators{
void setNum(){
operand1 = operand2%2; // operand1 lost its effectively final property here because it gets its second assignment
}
int add(){
return rem + operand2; // does not compile because rem is not effectively final
}
int multiply(){
return rem * operand1; // does not compile because both rem and operand1 are not effectively final
}
}
}
}
final
컴파일 오류를 발생시키지 않고 선언에 키워드를 추가 할 수 없으면 효과적으로 final 이 아닙니다 . 이 변수는 "변수가 효과적으로 최종 변수 인 경우 최종 수정자를 선언에 추가해도 컴파일 타임 오류가 발생하지 않습니다."라는 반대 문입니다.
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
// The following statement causes the compiler to generate
// the error "local variables referenced from a lambda expression
// must be final or effectively final" in statement A:
//
// x = 99;
}
}
}
다른 사람들이 말했듯이, 초기화 된 후에 값이 변경되지 않는 변수 또는 매개 변수는 사실상 최종입니다. 위의 코드 x
에서 내부 클래스 의 값을 변경 FirstLevel
하면 컴파일러에서 오류 메시지를 표시합니다.
람다 식에서 참조되는 지역 변수는 최종적이거나 효과적으로 최종적이어야합니다.
final
수정자를 지역 변수에 추가 할 수 있다면 사실상 최종 변수 입니다.
람다 식에 액세스 할 수 있습니다
정적 변수
인스턴스 변수
효과적으로 최종 분석법 파라미터
효과적으로 최종 지역 변수.
또한
effectively final
변수는 값이 변경되지 않습니다 변수이지만,이 선언되지 않은final
키워드.
출처 : Java로 시작하기 : 제어 구조에서 객체 (6 판)까지, Tony Gaddis
또한 final
처음 사용하기 전에 정확히 한 번 초기화된다는 의미를 잊지 마십시오 .
변수를 선언 final
하거나 선언하지 final
않지만 효과적으로 최종적으로 유지하면 다른 바이트 코드로 (컴파일러에 따라) 발생할 수 있습니다.
작은 예를 살펴 보겠습니다.
public static void main(String[] args) {
final boolean i = true; // 6 // final by declaration
boolean j = true; // 7 // effectively final
if (i) { // 9
System.out.println(i);// 10
}
if (!i) { // 12
System.out.println(i);// 13
}
if (j) { // 15
System.out.println(j);// 16
}
if (!j) { // 18
System.out.println(j);// 19
}
}
main
메소드 의 해당 바이트 코드 (Windows 64 비트의 Java 8u161) :
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iconst_1
3: istore_2
4: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
7: iconst_1
8: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
11: iload_2
12: ifeq 22
15: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_2
19: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
22: iload_2
23: ifne 33
26: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
29: iload_2
30: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
33: return
해당 라인 번호 테이블 :
LineNumberTable:
line 6: 0
line 7: 2
line 10: 4
line 15: 11
line 16: 15
line 18: 22
line 19: 26
line 21: 33
우리가 라인에서 소스 코드를 참조로 12
, 13
, 14
바이트 코드에 표시되지 않습니다. 그 이유 i
는 true
상태이므로 변경하지 않을 것입니다. 따라서이 코드는 도달 할 수 없습니다 (이 답변 에서 더 ). 같은 이유로 줄의 코드 9
도 그리워합니다. 의 상태는 확실 i
하므로 평가할 필요가 없습니다 true
.
반면 변수 j
는 사실상 최종 변수이지만 같은 방식으로 처리되지 않습니다. 적용된 최적화는 없습니다. 의 상태 j
는 두 번 평가됩니다. 바이트 코드는 실제로 최종적인j
것에 관계없이 동일 합니다.
그러나 Java SE 8부터 로컬 클래스는 최종 또는 효과적으로 최종 블록의 로컬 변수 및 매개 변수에 액세스 할 수 있습니다.
이것은 Java 8에서 시작되지 않았으므로 오랫동안 사용했습니다. 이 코드는 (Java 8 이전) 합법적이었습니다.
String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String ann = str; // <---- error, must be final (IDE's gives the hint);
String ann = strFin; // <---- legal;
String str = "legal statement on java 7,"
+"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl.";
//we are forced to use another name than str
}
);
final
변수에 액세스 할 수 있지만 자바 8 도 그 것을 효과적으로 마지막.