source

익명 클래스에서 최종 변수만 액세스할 수 있는 이유는 무엇입니까?

goodcode 2022. 8. 30. 22:11
반응형

익명 클래스에서 최종 변수만 액세스할 수 있는 이유는 무엇입니까?

  1. a왜서 마지 지지 ?? 왜?can당방을 재할당하려면 해야 하나요?aonClick()인인회 회원 관관 관관 관??

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                int b = a*5;
    
            }
        });
    }
    
  2. 하나요?5 * a★★★★★★★★★★★★★★★★★? 내 말은...

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                 int b = a*5;
                 return b; // but return type is void 
            }
        });
    }
    

코멘트에서한 바와 같이 8에서는 이 중 .Java 8에서는 8이 무효가 됩니다.final를 암시할 수 있습니다.그러나 익명 내부 클래스 또는 람다 식에는 효과적인 최종 변수만 사용할 수 있습니다.


기본적으로 Java가 폐쇄를 관리하는 방식 때문입니다.

익명 내부 클래스의 인스턴스를 만들 때 해당 클래스 내에서 사용되는 모든 변수는 자동 생성된 생성자를 통해 해당 을 복사합니다.이것에 의해, C# 컴파일러와 같이, 「로컬 변수」의 논리 상태를 유지하기 위해서, 컴파일러가 다양한 타입을 자동 생성하지 않아도 됩니다.(C#이 어나니머스 함수로 변수를 캡처하면 실제로 변수를 캡처합니다.폐쇄는 메서드의 본체에서 볼 수 있는 방법으로 변수를 갱신할 수 있습니다.그 반대도 마찬가지입니다).

값이 익명 내부 클래스의 인스턴스에 복사되었기 때문에 변수가 메서드의 나머지 부분에서 수정될 수 있다면 이상하게 보일 수 있습니다. 오래된 변수와 함께 동작하는 것처럼 보이는 코드가 있을 수 있습니다(실제로 발생하므로...).다른 시간에 복사한 복사본으로 작업할 수 있습니다.)마찬가지로 익명의 내부 클래스 내에서 변경할 수 있는 경우 개발자는 이러한 변경사항이 둘러싸인 메서드의 본문 내에서 표시될 것으로 예상할 수 있습니다.

변수 final을 사용하면 이러한 모든 가능성이 제거됩니다.값을 전혀 변경할 수 없기 때문에 변경 내용이 표시될지 여부를 걱정할 필요가 없습니다.메서드와 익명 내부 클래스가 서로의 변경 내용을 볼 수 있도록 하는 유일한 방법은 설명의 가변 유형을 사용하는 것입니다.이것은 엔클로징 클래스 자체, 어레이, 가변 래퍼 유형일 수 있습니다.뭐 그런 거라도.기본적으로는 어떤 메서드와 다른 메서드 간의 통신과 비슷합니다.즉, 메서드의 파라미터에 대한 변경은 발신자에 의해 인식되지 않지만 파라미터에 의해 참조되는 오브젝트에 대한 변경은 인식됩니다.

자바 클로저와 C# 클로저의 자세한 비교에 관심이 있으시다면, 그것에 대해 더 자세히 설명하는 기사가 있습니다.이 답변에서는 Java 쪽에 초점을 맞추고 싶었습니다.

익명의 클래스가 외부 스코프의 데이터를 갱신할 수 있는 트릭이 있습니다.

private void f(Button b, final int a) {
    final int[] res = new int[1];
    b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            res[0] = a * 5;
        }
    });

    // But at this point handler is most likely not executed yet!
    // How should we now res[0] is ready?
}

그러나 동기 문제로 인해 이 기술은 그다지 좋지 않습니다.나중에 핸들러를 호출할 경우 1) 핸들러가 다른 스레드에서 호출된 경우 리셋에 대한 접근을 동기화해야 합니다 2) 리셋이 갱신되었음을 나타내는 플래그 또는 표시가 있어야 합니다.

그러나 이 트릭은 익명 클래스가 같은 스레드에서 즉시 호출되는 경우 정상적으로 작동합니다.예를 들어 다음과 같습니다.

// ...

final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);

// ...

어나니머스 클래스는 내부 클래스이며 엄격한 규칙은 내부 클래스(JLS 8.1.3)적용됩니다.

내부 클래스에서 사용되었지만 선언되지 않은 로컬 변수, 정식 메서드 매개 변수 또는 예외 핸들러 매개 변수는 final로 선언해야 합니다.내부 클래스에서 사용되지만 선언되지 않은 로컬 변수는 내부 클래스의 본문 앞에 반드시 할당해야 합니다.

jls 또는 jvms에 대한 이유나 설명은 아직 찾지 못했지만 컴파일러가 각 내부 클래스에 대해 별도의 클래스 파일을 만들고 이 클래스 파일(바이트 코드 레벨)에 선언된 메서드가 적어도 로컬 변수 값에 액세스할 수 있는지 확인해야 합니다.

(John이 완전한 답을 가지고 있습니다.JLS 규칙에 관심이 있을 수 있기 때문에 삭제하지 않은 상태로 둡니다.)

클래스 수준 변수를 생성하여 값을 가져올 수 있습니다.제 말은.

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

이제 K의 가치를 얻어 원하는 곳에 사용할 수 있습니다.

그 이유에 대한 답변은 다음과 같습니다.

로컬 내부 클래스 인스턴스는 Main 클래스에 연결되며 포함하는 메서드의 최종 로컬 변수에 액세스할 수 있습니다.인스턴스가 포함 메서드의 최종 로컬을 사용하는 경우 변수가 범위를 벗어났더라도 변수는 인스턴스 생성 시 유지된 값을 유지합니다(이것은 사실상 Java의 조잡하고 제한된 버전의 폐쇄입니다).

로컬 내부 클래스는 클래스 또는 패키지의 멤버가 아니기 때문에 액세스레벨로 선언되지 않습니다(단, 그 멤버는 일반 클래스와 같은 액세스레벨을 가지고 있습니다).

이 제한의 이유를 이해하려면 다음 프로그램을 고려하십시오.

public class Program {

    interface Interface {
        public void printInteger();
    }
    static Interface interfaceInstance = null;

    static void initialize(int val) {
        class Impl implements Interface {
            @Override
            public void printInteger() {
                System.out.println(val);
            }
        }
        interfaceInstance = new Impl();
    }

    public static void main(String[] args) {
        initialize(12345);
        interfaceInstance.printInteger();
    }
}

interfaceInstanceinitialize 메서드가 반환된 후에도 메모리에 남지만 파라미터 val은 반환되지 않습니다.JVM은 범위 밖의 로컬 변수에 액세스할 수 없으므로 Java는 다음 호출을 통해interfaceInstance 내의 같은 이름의 암묵적인 필드에 val 을 복사함으로써 정수가 동작합니다.interfaceInstance는 로컬파라미터의 값을 캡처했다고 합니다.파라미터가 최종값이 아닐 경우(또는 사실상 최종값이 아닐 경우) 값이 변경되어 캡처된 값과 동기화되지 않아 의도하지 않은 동작이 발생할 수 있습니다.

Java에서 변수는 파라미터뿐만 아니라 클래스 레벨의 필드로서도 최종값이 될 수 있습니다.

public class Test
{
 public final int a = 3;

또는 로컬 변수로서, 예를 들어

public static void main(String[] args)
{
 final int a = 3;

익명 클래스에서 변수에 액세스하여 수정할 경우 변수를 둘러싸는 클래스의 클래스 수준 변수로 만들 수 있습니다.

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

변수를 최종 값으로 지정하고 새 값을 지정할 수 없습니다. final즉, 가치는 변하지 않고 최종적인 것입니다.

최종판이기 때문에 Java는 로컬 익명의 클래스에 안전하게 복사할 수 있습니다.int에 대한 참조는 얻을 수 없습니다(특히 Java의 int와 같은 프리미티브에 대한 참조는 할 수 없기 때문에 오브젝트에 대한 참조만 할 수 있습니다).

어나니머스 클래스의 a라는 암묵적인 int에 a의 값을 복사하기만 하면 됩니다.

액세스가 로컬 최종변수로만 제한되는 이유는 모든 로컬 변수에 액세스할 수 있는 경우 먼저 내부 클래스가 액세스할 수 있는 개별 섹션에 복사해야 하며, 여러 개의 가변 로컬 변수 복사본을 유지하면 데이터가 일관되지 않을 수 있기 때문입니다.최종 변수는 불변하기 때문에 어떤 수의 복사본도 데이터의 일관성에 영향을 미치지 않습니다.

메서드 본문 내에 어나니머스 내부 클래스가 정의되어 있는 경우 해당 메서드의 범위에서 final로 선언된 모든 변수는 내부 클래스 내에서 액세스할 수 있습니다.스칼라 값의 경우 할당 후 최종 변수 값은 변경할 수 없습니다.개체 값의 경우 참조를 변경할 수 없습니다.이를 통해 Java 컴파일러는 런타임에 변수 값을 "캡처"하고 복사본을 내부 클래스에 필드로 저장할 수 있습니다.외부 메서드가 종료되고 스택 프레임이 제거되면 원래 변수는 사라지지만 내부 클래스의 개인 복사본은 클래스 자체 메모리에 유지됩니다.

(http://en.wikipedia.org/wiki/Final_%28Java%29)

비노믹 내부 클래스 내의 메서드는 생성된 스레드가 종료된 후에 호출될 수 있습니다.이 예에서 inner 클래스는 이벤트디스패치 스레드에서 호출되며 작성한 스레드와 같은 스레드에서는 호출되지 않습니다.따라서 변수의 범위는 달라집니다.따라서 이러한 가변 할당 범위 문제를 보호하려면 최종 문제를 선언해야 합니다.

private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}

Jon이 구현 세부 정보를 가지고 있기 때문에, JVM이 자신의 액티베이션이 종료된 기록에 대한 쓰기를 처리하지 않으려 한다는 것도 가능한 답변입니다.

람다를 적용하는 대신 특정 위치에 저장하고 나중에 실행하는 사용 사례를 고려해 보십시오.

Smalltalk에서는 이러한 수정을 하면 불법 스토어가 제기되었던 것으로 기억합니다.

이 코드를 사용해 보세요.

어레이 목록을 만들고 그 안에 값을 입력한 후 반환합니다.

private ArrayList f(Button b, final int a)
{
    final ArrayList al = new ArrayList();
    b.addClickHandler(new ClickHandler() {

         @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             al.add(b);
        }
    });
    return al;
}

Java 익명 클래스는 Javascript closure와 매우 유사하지만 Java는 이를 다른 방식으로 구현합니다.(안데르센 답변 확인)

따라서 자바스크립트 배경에서 발생하는 이상한 동작과 자바 개발자를 혼동하지 않도록 하기 위해.그래서 어쩔 수 없이 우리에게final이것은 JVM의 제한이 아닙니다.

아래 Javascript의 예를 보겠습니다.

var add = (function () {
  var counter = 0;

  var func = function () {
    console.log("counter now = " + counter);
    counter += 1; 
  };

  counter = 100; // line 1, this one need to be final in Java

  return func;

})();


add(); // this will print out 100 in Javascript but 0 in Java

Javascript에서는counter값이 100이 됩니다.이것은 1개뿐이기 때문입니다.counter처음부터 끝까지 변수입니다.

하지만 자바에서, 만약 없다면final출력됩니다.0내부 오브젝트가 작성되고 있는 동안,0값이 내부 클래스 객체의 숨겨진 속성에 복사됩니다.(여기에는 로컬 메서드와 내부 클래스 숨김 속성의 두 정수 변수가 있습니다.)

따라서 행 1과 같이 내부 객체를 작성한 후 변경된 내용은 내부 객체에 영향을 주지 않습니다.따라서 (Java와 Javascript 사이에서) 두 가지 다른 결과와 행동을 혼동하게 됩니다.

그렇기 때문에 Java는 최종판결을 강요하기 때문에 데이터는 처음부터 끝까지 일관성이 있다고 생각합니다.

자바final내부의 변수inner class[대략]

내부 클래스는 사용할 수 밖에 사용할 수 없습니다.

  1. 외부 클래스로부터의 참조
  2. 유형인 "Colority of Scope")Object...)
  3. value(예: value:int...) 타입은 최종 참조 타입으로 래핑할 수 있습니다. IntelliJ IDEA하나의 요소 배열로 변환하는 데 도움이 됩니다.

의 경우non static nested )inner class는 컴파일러 - 클래스 - )에 의해 됩니다.<OuterClass>$<InnerClass>.class생성되고 경계 매개변수가 생성자[Local variable on stack] 전달됩니다. 이것은 닫힘과[휙] 유사합니다.

final variable은 재할당할 수 없는 변수입니다.상태를 수정하여 최종 참조 변수를 변경할 수 있습니다.

가능하다면 이상할 것이다 왜냐하면 프로그래머로서 이렇게 만들 수 있기 때문이다.

//Not possible 
private void foo() {

    MyClass myClass = new MyClass(); //Case 1: myClass address is 1
    int a = 5;                       //Case 2: a = 5

    //just as an example
    new Button().addClickHandler(new ClickHandler() {
        
        @Override
        public void onClick(ClickEvent event) {

            /*
            myClass.something(); //<- what is the address - 1 or 2?
            int b = a; //<- what is the value - 5 or 10 ?

            //illusion that next changes are visible for Outer class
            myClass = new MyClass();
            a = 15;
            */
        }
    });

    myClass = new MyClass(); //Case 1: myClass address is 2
    int a = 10; //Case 2: a = 10
}

어쩌면 이 속임수가 너에게 아이디어를 줄지도 몰라.

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);

언급URL : https://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class

반응형