Backend/Java

[안드로이드/자바] 익명 내부 클래스 변수 범위(final)

비비빅B 2020. 5. 19. 17:39

오류

public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText input1 = (EditText) findViewById(R.id.input1);	// EditText 선언과 동시에 초기화
        Button btn1 = (Button) findViewById(R.id.btn1);
        
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                num1 = input1.getText().toString();		// 오류 발생
            }
        });
    }
}

error: local variable input1 is accessed from within inner class; needs to be declared final num1 = input1.getText().toString();

  • 간단한 계산기 만드는 과정에서 오류


해결책

public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		// final 상수
        final EditText input1 = (EditText) findViewById(R.id.input1);	// EditText 선언과 동시에 초기화
        Button btn1 = (Button) findViewById(R.id.btn1);
        
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                num1 = input1.getText().toString();		// 오류 발생
            }
        });
    }
}

// 또는 1행과 2행 사이에 멤버변수 선언
public class MainActivity extends AppCompatActivity {
	EditText input1;	// 인스턴스 변수

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        input1 = (EditText) findViewById(R.id.input1);	// 초기화
        -----------------------------
"An anonymous class cannot access local variables in its enclosing scope that are not declared as 
final or effectively final"

-- Note: "effectively final" is something new introduced in Java SE 8. It is defined as a variable or parameter that is not declared as final, whose value is never changed after it is initialized. --

But what if, in the closure, we are only reading from the variable and not writing to it? Does it still need to be declared final?

If you are using Java 7 or below, the answer is "yes".

In Java 8, if we are only accessing but not changing the variable, the variable is "effectively final" and does not need to be declared final.
  • 익명 클래스는 자신을 감싸는 블록안의 지역변수는 참조할 수 없음

  • 변수를 final 상수로 선언하든가, 멤버변수로 만들어야 함

  • 단, 자바 8부터는 변수 값을 바꾸지 않고 단순 참조만 한다면 final 상수로 만들지 않아도 참조 가능


이유

Java wants the developer to use the final keyword on any variables that are going to be modified in the inner class. This prevents us from thinking the things we change in the inner class will persist in the enclosing scope.
  • 자바는 개발자들이 내부 클래스에서 외부 변수 값을 변경하는 것을 원치 않음

  • 왜냐하면 비직관적인 경과가 나올 수 있기 때문

  • 자바는 유연성을 약간 포기하는 대신 직관성을 높이는 철학을 택한 것임

예를 들자면, 자바스크립트에서 var는 for을 포함하는 scope를 가지기 때문에

var i;
for (i = 0; i < 10; i++) {
  setTimeout(function() {	// 0.1초 후에 i 프린트 하라는 함수
    console.log(i);
  }, 100);
}

// 10, 10, 10, 10, 10, 10, 10 ...
  • 위와 같은 코드를 실행하면 마지막 for문을 돌면서 i가 10이 됨

  • var i가 10이 되어 for문이 종료되고도 남은 0.1초 뒤에 전부 10을 출력함

  • 하지만 자바에서는 이런 코드를 오류로 인식해서 1행 var를 상수로 바꾸라던가, 멤버변수로 만들라고 오류를 띄움


  • 또 다른 이유로는 멀티스레드 기능 때문임

  • 만약 지역변수가 변경가능하다면 메서드내의 지역변수가 멀티스레드 상태에서 공유변수가 됨

  • 그 상황에서 변수 값이 변한다면 예상치 못한 상황이 발생될 수 있음

  • 또 다른 이유로는 지역 내부 클래스에서 지역 변수의 유효성 때문

     


final 문제점

public class RadiusStuff {	// outer class
    string radius = "10";	// 멤버 변수, 상수X

    public void start(Stage stage) {	//	메소드, 15행가지 하나의 블록(scope)
    Button submit = new Button("Submit");
    
    submit.setOnAction(new EventHandler<ActionEvent>() {
        // 내부 클래스, 익명 함수
        @Override
        public void handle(ActionEvent e) {
            submit.setText(radius);
        }
    });
    string radius = "15";	// radius 값 변경
  }
}
Referring back to Oracle's documentation we see that our inner class "
cannot access local variables in its enclosing scope that are not declared as 
final..."

So why would an instance variable be an exception? Note the words "local variable". This only applies to variables declared within the method that the inner class is in, also known as the "enclosing scope".

So does this mean an anonymous inner class can change an instance variable, and have those changes persist outside of the inner class? Yes.
  • final로 선언했기 때문에 변수를 바꿀 수 없음

  • 따라서  final보다는 멤버변수(인스턴스 변수)에 선언하는 것이 더 좋음


https://blog.brendanlacanette.com/2015/08/variable-is-accessed-within-inner-class.html

https://okky.kr/article/500829

'Backend > Java' 카테고리의 다른 글

Repository와 DAO 차이?  (0) 2021.01.14
[Spring Boot] 유효성 검사  (0) 2020.12.16
[JSP] 쿼리문 실행 오류  (0) 2020.07.09
[JSP] 한글 인코딩  (0) 2020.05.28
[안드로이드] 키보드 리스너 정의  (0) 2020.05.20