다른 메소드에 정의 된 내부 클래스 내에서 비 ​​최종 변수를 참조 할 수 없습니다.


247

편집 : 타이머를 통해 여러 번 실행되는 여러 변수의 값을 변경해야합니다. 타이머를 통해 반복 할 때마다 값을 계속 업데이트해야합니다. 값을 업데이트 할 수 없으므로 값을 final로 설정할 수 없지만 아래의 초기 질문에서 설명하는 오류가 발생합니다.

이전에 아래 내용을 작성했습니다.

"다른 방법으로 정의 된 내부 클래스 내의 비 최종 변수를 참조 할 수 없습니다"라는 오류가 발생합니다.

이것은 price라고하는 가격과 priceObject라는 가격에 발생합니다. 내가 왜이 문제가 발생하는지 아십니까? 최종 선언이 필요한 이유를 이해하지 못합니다. 또한 내가하려는 일을 볼 수 있다면이 문제를 해결하기 위해 무엇을해야합니까?

public static void main(String args[]) {

    int period = 2000;
    int delay = 2000;

    double lastPrice = 0;
    Price priceObject = new Price();
    double price = 0;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);
}

내가 묻는 것은 지속적으로 업데이트 할 수있는 타이머에 변수를 얻는 방법입니다.
Ankur

1
@Ankur : 간단한 대답은 "아니오"입니다. 그러나 내부 클래스를 사용하여 원하는 효과를 얻을 수 있습니다. @petercardona의 답변을 참조하십시오.
Stephen C

답변:


197

Java는 여기에서 사용하는 것과 같은 익명 클래스를 사용하는 것이 일종의 클로저처럼 보이지만 진정한 클로저를 지원하지 않습니다 new TimerTask() { ... }.

편집 - 아래 주석을 참조하십시오-KeeperOfTheSoul이 지적한대로 다음은 올바른 설명이 아닙니다.

이것이 작동하지 않는 이유입니다.

변수 lastPrice와 가격은 main () 메소드의 로컬 변수입니다. 익명 클래스로 만든 객체는 main()메서드가 반환 될 때까지 지속될 수 있습니다 .

main()메소드가 리턴 (같은 지역 변수 lastPrice와는 price) 스택에서 정리됩니다, 그래서 그들은 이후 더 이상 존재하지 않을 것이다 main()돌아갑니다.

그러나 익명 클래스 객체는 이러한 변수를 참조합니다. 익명 클래스 객체가 변수를 정리 한 후 변수에 액세스하려고하면 상황이 끔찍하게 잘못 될 수 있습니다.

함으로써 lastPrice그리고 price final, 그들은 더 이상 변수 만 상수는 정말 아니다. 그러면 컴파일러는 익명 클래스 의 사용 lastPriceprice익명 클래스 의 사용을 상수 값 (물론 컴파일 타임에)으로 대체 할 수 있으며 더 이상 존재하지 않는 변수에 액세스하는 데 문제가 없습니다.

클로저를 지원하는 다른 프로그래밍 언어는 이러한 변수를 특수하게 처리하여 메소드가 종료 될 때 변수가 손상되지 않도록하여 클로저가 여전히 변수에 액세스 할 수 있도록합니다.

@Ankur : 당신은 이것을 할 수 있습니다 :

public static void main(String args[]) {
    int period = 2000;
    int delay = 2000;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        // Variables as member variables instead of local variables in main()
        private double lastPrice = 0;
        private Price priceObject = new Price();
        private double price = 0;

        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);      
}

34
사실은 아니지만 Java는 문제의 변수에 대한 캡처를 생성하여 런타임 값을 캡처합니다. 대리인의 값을 캡처하여 .Net에서 가능한 이상한 부작용을 피하고 싶었습니다. 외부 메소드의 값을 가져 오면 이제 델리게이트는 Java가 피하려는이 동작의 C # 예제에 대해 stackoverflow.com/questions/271440/c-captured-variable-in-loop 의 새로운 값을 참조 합니다.
Chris Chilvers

14
그것은 "이상한 부작용"이 아니며 사람들이 기대하는 정상적인 행동이며 캡처를 생성하지 않기 때문에 Java가 제공 할 수없는 행동 입니다. 이 문제를 해결하려면 익명 클래스에 사용 된 로컬 변수가 최종 변수 여야합니다.
Michael Borgwardt

12
Jesper, 아마도 위의 내용이 잘못되었다는 메시지를 표시하는 대신 답변의 잘못된 부분을 편집해야합니다.
James McMahon

19
실제로 Java는 클로저를 지원하지 않습니다. 클로저를 지원하는 언어는 전체 로컬 환경 (즉, 현재 스택 프레임에 정의 된 로컬 변수 집합)을 힙 객체로 저장하여이를 수행합니다. Java는 이것을 지원하지 않습니다 (언어 디자이너는 구현하려고하지만 시간이 부족합니다). 해결 방법으로 로컬 클래스가 인스턴스화 될 때마다 참조하는 모든 로컬 변수 값이 힙에 복사됩니다 . 그러나 JVM은 값을 로컬 변수와 동기화하여 유지할 수 없으므로 최종 값이어야합니다.
Taymon

64
이 답변에 "KeeperOfTheSoul"이라는 사람이 없기 때문에이 답변은 완전히 혼동됩니다. 답을 수정해야합니다.
Adam Parkin

32

익명 대리자가 참조한 Java 변수의 클로저로 이상한 부작용을 피하려면 lastPrice타이머 작업 내에서 참조 하고 가격 을 정하기 위해 최종으로 표시해야합니다.

변경하기를 원하기 때문에 분명히 효과가 없을 것입니다.이 경우 클래스 내에서 캡슐화하는 것을 봐야합니다.

public class Foo {
    private PriceObject priceObject;
    private double lastPrice;
    private double price;

    public Foo(PriceObject priceObject) {
        this.priceObject = priceObject;
    }

    public void tick() {
        price = priceObject.getNextPrice(lastPrice);
        lastPrice = price;
    }
}

이제 마지막으로 새 Foo를 만들고 타이머에서 .tick를 호출하십시오.

public static void main(String args[]){
    int period = 2000;
    int delay = 2000;

    Price priceObject = new Price();
    final Foo foo = new Foo(priceObject);

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            foo.tick();
        }
    }, delay, period);
}

1
아니면 그냥 Foo 구현 Runnable ..?
vidstige

18

익명 클래스를 사용할 때는 포함하는 클래스에서만 최종 변수에 액세스 할 수 있습니다. 따라서 final로 사용되는 변수를 선언하거나 ( lastPriceprice를 변경 하고 있으므로 옵션이 아님 ) 익명 클래스를 사용하지 않아야합니다.

따라서 옵션은 변수를 전달하고 정상적인 방식으로 사용할 수있는 실제 내부 클래스를 만드는 것입니다.

또는:

lastPrice가격 변수에 대한 빠른 (그리고 내 견해로는 추악한) 해킹이 있습니다.

final double lastPrice[1];
final double price[1];

익명 클래스에서 다음과 같이 값을 설정할 수 있습니다

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];

14

이미 제공하려는 것을 할 수없는 이유에 대한 좋은 설명. 해결책으로 다음을 고려하십시오.

public class foo
{
    static class priceInfo
    {
        public double lastPrice = 0;
        public double price = 0;
        public Price priceObject = new Price ();
    }

    public static void main ( String args[] )
    {

        int period = 2000;
        int delay = 2000;

        final priceInfo pi = new priceInfo ();
        Timer timer = new Timer ();

        timer.scheduleAtFixedRate ( new TimerTask ()
        {
            public void run ()
            {
                pi.price = pi.priceObject.getNextPrice ( pi.lastPrice );
                System.out.println ();
                pi.lastPrice = pi.price;

            }
        }, delay, period );
    }
}

아마도 그보다 더 나은 디자인을 할 수있는 것처럼 보이지만 아이디어는 변경되지 않은 클래스 참조 내에서 업데이트 된 변수를 그룹화 할 수 있다는 것입니다.


11

익명 클래스를 사용하면 실제로 "이름이없는"중첩 클래스를 선언합니다. 중첩 클래스의 경우 컴파일러는 인수로 사용하는 모든 변수를 인수로 사용하는 생성자를 사용하여 새 독립 실행 형 공용 클래스를 생성합니다 ( "명명 된"중첩 클래스의 경우 항상 원본 / 클래 싱 클래스의 인스턴스 임). 런타임 환경에는 중첩 클래스에 대한 개념이 없기 때문에 중첩 클래스에서 독립형 클래스로 (자동) 변환해야합니다.

이 코드를 예로 들어 보겠습니다.

public class EnclosingClass {
    public void someMethod() {
        String shared = "hello"; 
        new Thread() {
            public void run() {
                // this is not valid, won't compile
                System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

이것은 컴파일러가 후드에서하는 일이기 때문에 작동하지 않습니다.

public void someMethod() {
    String shared = "hello"; 
    new EnclosingClass$1(shared).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

원래의 익명 클래스는 컴파일러가 생성하는 독립형 클래스로 대체됩니다 (코드는 정확하지 않지만 좋은 아이디어를 제공해야합니다).

public class EnclosingClass$1 extends Thread {
    String shared;
    public EnclosingClass$1(String shared) {
        this.shared = shared;
    }

    public void run() {
        System.out.println(shared);
    }
}

보시다시피 독립형 클래스는 공유 객체에 대한 참조를 보유하고 있으며 Java의 모든 것은 값을 전달한다는 점을 기억하십시오. 따라서 EnclosingClass의 'shared'참조 변수가 변경 되더라도 가리키는 인스턴스는 수정되지 않습니다. 익명 클래스의 Enclosing $ 1과 같은 다른 모든 참조 변수는이를 인식하지 못합니다. 이것이 컴파일러가이 '공유'변수를 최종 변수로 선언하도록하는 주된 이유입니다. 따라서 이러한 유형의 동작은 이미 실행중인 코드에 영향을 미치지 않습니다.

이제 이것은 익명 클래스 내에서 인스턴스 변수를 사용할 때 발생하는 현상입니다 (문제를 해결하고 로직을 "인스턴스"메서드 또는 클래스 생성자로 이동하려면 수행해야합니다).

public class EnclosingClass {
    String shared = "hello";
    public void someMethod() {
        new Thread() {
            public void run() {
                System.out.println(shared); // this is perfectly valid
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

컴파일러가 코드를 수정하여 새로 생성 된 클래스 Enclosing $ 1이 인스턴스화 된 EnclosingClass 인스턴스에 대한 참조를 보유하기 때문에 이것은 잘 컴파일됩니다 (이것은 단지 표현 일뿐입니다).

public void someMethod() {
    new EnclosingClass$1(this).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

public class EnclosingClass$1 extends Thread {
    EnclosingClass enclosing;
    public EnclosingClass$1(EnclosingClass enclosing) {
        this.enclosing = enclosing;
    }

    public void run() {
        System.out.println(enclosing.shared);
    }
}

이와 같이 EnclosingClass에서 'shared'참조 변수가 재 할당되고 Thread # run ()을 호출하기 전에이 문제가 발생하면 EnclosingClass $ 1 # enclosing 변수가 참조를 유지하므로 "other hello"가 두 번 인쇄됩니다. 선언 된 클래스의 개체에 대한 속성이므로 해당 개체의 모든 특성에 대한 변경 내용은 EnclosingClass $ 1 인스턴스에 표시됩니다.

주제에 대한 자세한 내용은 다음과 같은 우수한 블로그 게시물을 참조하십시오 (저는 작성하지 않음). http://kevinboone.net/java_inner.html


로컬 변수 'shared'가 가변 객체 인 경우 어떻게됩니까? 귀하의 설명에 따라 '최종'을 선언해도 도움이되지 않습니다.
sactiw

"shared"를 final로 선언하면 최종 변수가 참조하는 객체의 상태를 수정할 수 있지만이 특정 예제에서는 "shared"변수의 값을 변경할 수 없기 때문에 작동하지 않습니다. OP가 원했던 것입니다) 익명 클래스 내에서 사용할 수는 있지만 값은 변경되지 않습니다 (최종으로 선언되기 때문에). 변수와 변수가 보유한 실제 값 (기본 요소 또는 힙의 객체에 대한 참조 일 수 있음)의 차이를 알아 두는 것이 중요합니다.
emerino

>>> 그러나 값은 변하지 않습니다 . 요점을 놓친 것 같습니다. 즉 최종 참조 변수가 변경 가능한 객체를 가리키는 경우 여전히 업데이트 할 수 있지만 익명 클래스는 얕은 사본을 생성하므로 변경 사항은 익명으로 반영됩니다 수업. 다시 말해서, 여기서 상태는 동기화되어있다. 여기서 OP는 공유 변수 (기본 유형)를 수정하고 OP를 변경 가능 오브젝트 아래에 값을 랩핑하고 해당 변경 가능 오브젝트를 공유해야합니다.
sactiw

1
물론 OP는 변경 가능한 객체 아래에 필요한 값을 래핑하고 변수를 final로 선언하고 대신 사용할 수 있습니다. 그러나 그는 변수를 현재 클래스의 속성으로 선언하여 추가 객체를 사용하지 않을 수 있습니다 (답변에서 지적하고 설명 함). 공유 변수의 값을 수정하기 위해 배열을 사용하는 것처럼 변경 가능한 객체를 강요하는 것은 좋은 생각이 아닙니다.
emerino

7

이 문제를 우연히 발견하면 생성자를 통해 객체를 내부 클래스로 전달합니다. 프리미티브 또는 변경 불가능한 객체 (이 경우와 같이)를 전달해야하는 경우 래퍼 클래스가 필요합니다.

편집 : 실제로 익명 클래스를 사용하지 않고 적절한 하위 클래스를 사용합니다.

public class PriceData {
        private double lastPrice = 0;
        private double price = 0;

        public void setlastPrice(double lastPrice) {
            this.lastPrice = lastPrice;
        }

        public double getLastPrice() {
            return lastPrice;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getPrice() {
            return price;
        }
    }

    public class PriceTimerTask extends TimerTask {
        private PriceData priceData;
        private Price priceObject;

        public PriceTimerTask(PriceData priceData, Price priceObject) {
            this.priceData = priceData;
            this.priceObject = priceObject;
        }

        public void run() {
            priceData.setPrice(priceObject.getNextPrice(lastPrice));
            System.out.println();
            priceData.setLastPrice(priceData.getPrice());

        }
    }

    public static void main(String args[]) {

        int period = 2000;
        int delay = 2000;

        PriceData priceData = new PriceData();
        Price priceObject = new Price();

        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new PriceTimerTask(priceData, priceObject), delay, period);
    }

2

Java 언어 사양에 나와 있기 때문에 비 최종 변수를 참조 할 수 없습니다. 8.1.3부터 ​​:
"내부 클래스에서 선언되었지만 선언되지 않은 모든 로컬 변수, 공식 메소드 매개 변수 또는 예외 핸들러 매개 변수는 final로 선언되어야합니다." 전체 단락.
로컬 변수의 수정을 예약하는 것은 이상한 생각입니다. 함수를 떠날 때 지역 변수가 존재하지 않습니다. 어쩌면 클래스의 정적 필드가 더 좋을까요?


2

방금 저자의 의도에 따라 무언가를 처리 할 무언가를 썼습니다 . 가장 좋은 방법 은 생성자가 모든 객체를 가져 와서 구현 된 메소드에서 해당 생성자 객체를 사용하는 것입니다.

그러나 일반 인터페이스 클래스를 작성하는 경우 객체 또는 더 나은 객체 목록을 전달해야합니다. 이것은 Object [] 또는 Object ... 로 수행 할 수 있습니다. Object ... 호출하기가 쉽기 때문입니다.

아래 예제 예제를 참조하십시오.

List<String> lst = new ArrayList<String>();
lst.add("1");
lst.add("2");        

SomeAbstractClass p = new SomeAbstractClass (lst, "another parameter", 20, true) {            

    public void perform( ) {                           
        ArrayList<String> lst = (ArrayList<String>)getArgs()[0];                        
    }

};

public abstract class SomeAbstractClass{    
    private Object[] args;

    public SomeAbstractClass(Object ... args) {
        this.args = args;           
    }      

    public abstract void perform();        

    public Object[] getArgs() {
        return args;
    }

}

기본적으로이를 지원하는 Java 클로저에 대한이 게시물을 참조하십시오. http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html

버전 1은 오토 캐스팅을 통해 비 최종 클로저 전달을 지원합니다.
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java

    SortedSet<String> sortedNames = new TreeSet<String>();
    // NOTE! Instead of enforcing final, we pass it through the constructor
    eachLine(randomFile0, new V1<String>(sortedNames) {
        public void call(String line) {
            SortedSet<String> sortedNames = castFirst();  // Read contructor arg zero, and auto cast it
            sortedNames.add(extractName(line));
        }
    });

2

익명 클래스 내에서 메서드 호출의 값을 변경하려는 경우 해당 "value"는 실제로입니다 Future. 구아바를 사용하면

...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){

    public void run(){
        ...
        myvalue.set(value);
        ...
    }
 }

 return myvalue.get();

2

내가 알았던 한 가지 해결책은 언급되지 않았다. 메소드 내에서 새 스레드를 실행하려고 시도하는이 문제가 발생했습니다 new Thread(){ Do Something }.

doSomething()다음에서 전화 하면 작동합니다. 반드시 선언 할 final필요는 없으며, 변수의 범위를 변경하여 내부 클래스 이전에 수집되지 않도록해야합니다. 이것은 물론 프로세스가 크지 않고 범위를 변경하면 일종의 충돌이 발생할 수 있습니다. 변수가 최종 / 상수가 아니기 때문에 변수를 최종으로 만들고 싶지 않았습니다.

public class Test
{

    protected String var1;
    protected String var2;

    public void doSomething()
    {
        new Thread()
        {
            public void run()
            {
                System.out.println("In Thread variable 1: " + var1);
                System.out.println("In Thread variable 2: " + var2);
            }
        }.start();
    }

}

1

변수가 최종 변수 여야한다면 변수 값을 다른 변수에 할당하고 THAT를 최종 변수로 만들어서 대신 사용할 수 있습니다.



1

외부 클래스 외부에서 변수를 선언 할 수 있습니다. 그런 다음 내부 클래스 내에서 변수를 편집 할 수 있습니다. 안드로이드에서 코딩하는 동안 때로는 비슷한 문제에 직면하여 변수를 전역으로 선언하면 효과가 있습니다.


이것은 실제로 질문에 대답하지 않습니다 ... 당신이 downvoted되고있는 이유입니다.
스튜어트 시글 러

0

당신은 할 수 있습니다 lastPrice, priceObject그리고 price익명의 내부 클래스의 필드를?


0

주요 관심사는 익명 클래스 인스턴스 내의 변수를 런타임에 확인할 수 있는지 여부입니다. 변수가 런타임 범위 내에 있다고 보장되는 한 변수를 최종 변수로 만들 필요는 없습니다. 예를 들어 updateStatus () 메서드 내에서 _statusMessage 및 _statusTextView라는 두 변수를 참조하십시오.

public class WorkerService extends Service {

Worker _worker;
ExecutorService _executorService;
ScheduledExecutorService _scheduledStopService;

TextView _statusTextView;


@Override
public void onCreate() {
    _worker = new Worker(this);
    _worker.monitorGpsInBackground();

    // To get a thread pool service containing merely one thread
    _executorService = Executors.newSingleThreadExecutor();

    // schedule something to run in the future
    _scheduledStopService = Executors.newSingleThreadScheduledExecutor();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    ServiceRunnable runnable = new ServiceRunnable(this, startId);
    _executorService.execute(runnable);

    // the return value tells what the OS should
    // do if this service is killed for resource reasons
    // 1. START_STICKY: the OS restarts the service when resources become
    // available by passing a null intent to onStartCommand
    // 2. START_REDELIVER_INTENT: the OS restarts the service when resources
    // become available by passing the last intent that was passed to the
    // service before it was killed to onStartCommand
    // 3. START_NOT_STICKY: just wait for next call to startService, no
    // auto-restart
    return Service.START_NOT_STICKY;
}

@Override
public void onDestroy() {
    _worker.stopGpsMonitoring();
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

class ServiceRunnable implements Runnable {

    WorkerService _theService;
    int _startId;
    String _statusMessage;

    public ServiceRunnable(WorkerService theService, int startId) {
        _theService = theService;
        _startId = startId;
    }

    @Override
    public void run() {

        _statusTextView = MyActivity.getActivityStatusView();

        // get most recently available location as a latitude /
        // longtitude
        Location location = _worker.getLocation();
        updateStatus("Starting");

        // convert lat/lng to a human-readable address
        String address = _worker.reverseGeocode(location);
        updateStatus("Reverse geocoding");

        // Write the location and address out to a file
        _worker.save(location, address, "ResponsiveUx.out");
        updateStatus("Done");

        DelayedStopRequest stopRequest = new DelayedStopRequest(_theService, _startId);

        // schedule a stopRequest after 10 seconds
        _theService._scheduledStopService.schedule(stopRequest, 10, TimeUnit.SECONDS);
    }

    void updateStatus(String message) {
        _statusMessage = message;

        if (_statusTextView != null) {
            _statusTextView.post(new Runnable() {

                @Override
                public void run() {
                    _statusTextView.setText(_statusMessage);

                }

            });
        }
    }

}

0

나를 위해 일한 것은 단지이 함수 외부에서 변수를 정의하는 것입니다.

주요 함수 직전에 선언하십시오.

Double price;
public static void main(String []args(){
--------
--------
}

그래도 작동하지 않습니다. 인스턴스 변수를 선언하고 사용하려면 기본 메소드 내에 인스턴스를 작성해야합니다. 좀 더 구체적이거나 정적 수정자를 'price'변수에 추가해야합니다.
emerino

0

변수를 정적으로 선언하고 className.variable을 사용하여 필요한 메소드에서 변수를 참조하십시오.


Non-static parameter cannot be referenced from a static context
stephen

@Shweta 로컬 변수 및 메소드 매개 변수는 '정적'으로 선언 할 수 없으며 메소드 (로컬 익명 클래스) 내의 클래스가 메소드 후에도 로컬 변수 및 메소드 매개 변수에 계속 액세스 할 수 있도록 구현 된 방식에 관한 것입니다 즉, '최종'복사본을 만들어 인스턴스 변수로 사용합니다.
sactiw

0

또 다른 설명입니다. 아래의이 예를 고려하십시오

public class Outer{
     public static void main(String[] args){
         Outer o = new Outer();
         o.m1();        
         o=null;
     }
     public void m1(){
         //int x = 10;
         class Inner{
             Thread t = new Thread(new Runnable(){
                 public void run(){
                     for(int i=0;i<10;i++){
                         try{
                             Thread.sleep(2000);                            
                         }catch(InterruptedException e){
                             //handle InterruptedException e
                         }
                         System.out.println("Thread t running");                             
                     }
                 }
             });
         }
         new Inner().t.start();
         System.out.println("m1 Completes");
    }
}

여기서 출력은

m1 완료

스레드 t 실행

스레드 t 실행

스레드 t 실행

................

이제 m1 () 메소드가 완료되고 참조 변수 o를 null에 할당합니다. 이제 외부 클래스 오브젝트는 GC에 적합하지만 실행중인 스레드 오브젝트와 (Has-A) 관계가있는 내부 클래스 오브젝트는 여전히 존재합니다. 기존 외부 클래스 객체가 없으면 기존 m1 () 메소드가 없을 가능성이 없으며 기존 m1 () 메소드가 없으면 로컬 변수가 존재할 가능성이 없지만 내부 클래스 객체가 m1 () 메소드의 로컬 변수를 사용하는 경우 모든 것이 자명합니다 .

이 문제를 해결하려면 로컬 변수의 복사본을 만든 다음 내부 클래스 객체를 사용하여 힙에 복사 한 다음 Java로 변경해야합니다 .Java는 실제로 변수가 아니기 때문에 최종 변수에 대해서만 수행합니다. 런타임에 아닙니다).


-1

위의 문제를 해결하기 위해 언어마다 다른 결정을 내립니다.

Java의 경우 솔루션은이 기사에서 보는 것과 같습니다.

C #의 경우 솔루션은 부작용을 허용하며 참조로 캡처하는 것이 유일한 옵션입니다.

C ++ 11의 경우 솔루션은 프로그래머가 결정을 내릴 수 있도록하는 것입니다. 그들은 가치 또는 참조로 캡처하도록 선택할 수 있습니다. 값으로 캡처하면 참조 된 변수가 실제로 다르기 때문에 부작용이 발생하지 않습니다. 참조로 캡처하면 부작용이 발생할 수 있지만 프로그래머는이를 인식해야합니다.


-2

변수가 최종 클래스가 아닌 경우 혼란 스럽기 때문에 변경 사항이 익명 클래스에서 선택되지 않으므로 변경되지 않습니다.

변수 'price'와 'lastPrice'를 최종적으로 만드십시오.

-- 편집하다

죄송합니다. 기능에 분명히 할당 할 필요는 없습니다. 새로운 지역 변수가 필요합니다. 어쨌든 누군가 지금 당신에게 더 나은 대답을했다고 생각합니다.


2
혼란 스러울뿐만 아니라 완전히 잘못되어 컴파일러에서 허용하지 않습니다.
Chii

그러나 필요할 때 어떻게 값을 변경합니까?
Ankur

혼란 스럽기 때문 만은 아닙니다. Java가 클로저를 지원하지 않기 때문입니다. 아래 답변을 참조하십시오. @Ankur : main ()에서 로컬 변수 대신 익명 클래스 객체의 변수를 변수로 만들 수 있습니다.
Jesper

그는 그것들을 수정하고 있으므로 최종적이 될 수 없습니다.
Robin

price와 lastPrice가 최종적인 경우 할당이 컴파일되지 않습니다.
Greg Mattes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.