다른 메서드에서 정의된 내부 클래스 내의 비최종 변수를 참조할 수 없습니다.
편집: 여러 변수가 타이머로 여러 번 실행되므로 값을 변경해야 합니다.타이머를 통해 반복할 때마다 값을 계속 업데이트해야 합니다.값을 final로 설정할 수 없습니다.이렇게 하면 값을 갱신할 수 없기 때문입니다만, 다음의 첫 번째 질문에 기재되어 있는 에러가 표시됩니다.
저는 이전에 다음과 같은 내용을 기재했습니다.
"다른 메서드로 정의된 내부 클래스 내의 비최종 변수를 참조할 수 없습니다"라는 오류가 나타납니다.
이것은 더블 콜드 프라이스와 price Object라는 프라이스에서 발생합니다.왜 이런 문제가 생기는지 아세요?나는 왜 내가 최종 선언을 해야 하는지 이해할 수 없다.또, 제가 무엇을 하려고 하는지 알 수 있다면, 이 문제를 피하기 위해 무엇을 해야 할까요?
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);
}
Java는 여기서 사용하는 것과 같은 익명 클래스를 사용하더라도 진정한 폐쇄를 지원하지 않습니다.new TimerTask() { ... }
는 일종의 는 일종의 종결처럼 보입니다.
편집 - 아래 코멘트를 참조해 주세요.Keeper Of The Soul이 지적한 바와 같이 다음 설명은 올바르지 않습니다.
이것이 동작하지 않는 이유입니다.
「」lastPrice
【가격】메인()【가격】 클래스로 로 만든 오브젝트가 anonymous class가 될 될 수 .main()
메서드가 반환됩니다.
?main()
, 변수(「」, 「」등)가 됩니다.lastPrice
★★★★★★★★★★★★★★★★★」price
main()
아온온다
그러나 익명 클래스 개체는 이러한 변수를 참조합니다.변수가 정리된 후 익명 클래스 개체가 변수에 액세스하려고 하면 상황이 끔찍하게 잘못됩니다.
lastPrice
★★★★★★★★★★★★★★★★★」price
final
더 이상 변수가 아니라 상수입니다. 후 는 단순히 할 수 .lastPrice
★★★★★★★★★★★★★★★★★」price
(물론 컴파일 시) 상수의 값을 가진 익명 클래스에서 더 이상 존재하지 않는 변수에 액세스하는 데 문제가 없습니다.
닫힘을 지원하는 다른 프로그래밍 언어에서는 이러한 변수를 특별히 처리하여 메서드가 종료될 때 변수가 파괴되지 않도록 함으로써 닫힘이 변수에 계속 액세스할 수 있도록 합니다.
@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);
}
이상한 최종적인 .하려면 , 「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);
}
어나니머스 클래스를 사용하는 경우 포함된 클래스에서만 최종 변수에 액세스할 수 있습니다.따라서 최종 사용 중인 변수를 선언하거나(last Price와 가격을 변경할 예정이므로 선택사항이 아님), 익명 클래스를 사용하지 마십시오.
따라서 실제 내부 클래스를 만들고 변수를 전달하여 일반 방식으로 사용할 수 있습니다.
또는 다음과 같이 입력합니다.
당신의 last Price와 가격변수에 대한 빠른 해킹(그리고 추악한)이 있습니다.그것은 그렇게 선언하는 것입니다.
final double lastPrice[1];
final double price[1];
익명의 클래스에서는, 다음과 같이 값을 설정할 수 있습니다.
price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];
당신이 하려고 하는 일을 할 수 없는 이유에 대한 좋은 설명들이 이미 제공되어 있다.해결책으로 다음 사항을 고려할 수 있습니다.
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 );
}
}
더 나은 설계를 할 수 있을 것 같지만, 그 아이디어는 업데이트된 변수를 변경되지 않는 클래스 참조 내에서 그룹화할 수 있다는 것입니다.
익명 클래스의 경우 실제로는 "이름 없는" 중첩 클래스를 선언합니다.중첩된 클래스의 경우, 컴파일러는 인수로 사용하는 모든 변수를 받아들이는 생성자를 가진 새로운 독립형 퍼블릭 클래스를 생성합니다("이름 있는" 중첩 클래스의 경우, 이것은 항상 원본/폐쇄 클래스의 인스턴스입니다.이는 런타임 환경에 중첩된 클래스에 대한 개념이 없기 때문에 중첩된 클래스에서 독립형 클래스로 자동 변환해야 하기 때문입니다.
다음 코드를 예로 들어 보겠습니다.
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의 모든 것은 pass-by-value이기 때문에 Enclosing Class의 참조 변수가 변경되어도 해당 참조 변수가 가리키는 인스턴스는 변경되지 않으며 다른 모든 참조 변수가 이를 가리키고 있습니다(익명 클래스의 참조 변수와 같습니다.$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);
}
}
이와 같이 Enclosing Class의 참조 변수가 재할당되면 Thread #run()에 대한 호출 전에 "other hello"가 두 번 인쇄됩니다. 왜냐하면 현재 Enclosing Class$1#Enclosing 변수는 선언된 클래스의 객체에 대한 참조를 유지하므로 해당 객체의 속성 변경은 모두 가시화되기 때문입니다.e부터 Enclosing Class$1 인스턴스까지.
이 주제에 대한 자세한 내용은 이 뛰어난 블로그 투고(제가 작성하지 않음)를 참조하십시오.http://kevinboone.net/java_inner.html
이 문제를 발견하면 컨스트럭터를 통해 오브젝트를 내부 클래스로 전달합니다.원시 개체 또는 불변의 개체를 전달해야 하는 경우(이 경우처럼) 래퍼 클래스가 필요합니다.
편집: 사실 익명 클래스는 전혀 사용하지 않지만 적절한 하위 클래스를 사용합니다.
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);
}
자바 언어 사양.1.3부터:
"사용되었지만 내부 클래스에서 선언되지 않은 로컬 변수, 정식 메서드 매개 변수 또는 예외 핸들러 매개 변수는 최종으로 선언해야 합니다."전체 단락.
을 사용하다저에 따르면 지역 변수를 수정하는 것은 이상한 생각이라고 합니다.함수를 종료하면 로컬 변수는 더 이상 존재하지 않습니다.래래의 정적드드? ?드 ????
나는 단지 저자의 의도에 따라 무언가를 처리하기 위해 뭔가를 썼다.가장 좋은 방법은 컨스트럭터가 모든 오브젝트를 가져가고 구현된 메서드에서 컨스트럭터 오브젝트를 사용하는 것입니다.
단, 범용 인터페이스 클래스를 작성할 경우 오브젝트 또는 더 나은 오브젝트 목록을 전달해야 합니다.이것은 오브젝트[] 또는 오브젝트...에 의해 실행할 수 있습니다.호출이 용이하기 때문입니다.
아래 예시를 참조하십시오.
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.javahttpsgithub.com/MSeifeddo//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));
}
});
클래스 하는 경우, 그 「value」는는 「value」가 됩니다.Future
Guava라고 쓸 수
...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){
public void run(){
...
myvalue.set(value);
...
}
}
return myvalue.get();
제가 알고 있는 해결책 중 하나는 클래스 변수를 사용하는 것입니다(틀린 경우가 아니라면 수정해 주십시오).내에서 하려고 할 때 이 하였습니다.new Thread(){ Do Something }
" "doSomething()
하다.final
변수 범위를 변경하면 inner 클래스 전에 수집되지 않습니다.이는 물론 프로세스가 크고 범위를 변경하면 충돌이 발생할 수 있는 경우를 제외하고는 다릅니다.결승/상수가 아니었기 때문에 변수 결승을 만들고 싶지 않았다.
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();
}
}
변수가 최종값이 될 수 없는 경우 변수 값을 다른 변수에 할당하고 해당 변수를 최종값으로 만들어 대신 사용할 수 있습니다.
ClassName.this.variableName을 사용하여 비최종 변수를 참조합니다.
외부 클래스 외부에 변수를 선언할 수 있습니다.그런 다음 내부 클래스 내에서 변수를 편집할 수 있습니다.Android에서 코딩할 때 비슷한 문제가 발생할 수 있기 때문에 변수를 글로벌로 선언하고 사용할 수 있습니다.
들들수??? can can can 를 만들 수 ?lastPrice
,priceObject
, , , , 입니다.price
명의의 내부 급? ???
주요 관심사는 익명 클래스 인스턴스 내의 변수를 런타임에 해결할 수 있는지 여부입니다.변수가 런타임 범위 내에 있음을 보증하는 한 변수를 최종으로 만들 필요는 없습니다.예를 들어 statusMessage와 _status라는2개의 변수를 참조해 주세요.updateStatus() 메서드 내의 TextView.
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);
}
});
}
}
}
이 함수 밖에 있는 변수를 정의하는 것이 효과적이었습니다.
주 함수가 선언하기 직전, 즉,
Double price;
public static void main(String []args(){
--------
--------
}
변수를 스태틱으로 선언하고 className.variable을 사용하여 필요한 메서드로 참조합니다.
또 다른 설명입니다.다음 예시를 참고해 주십시오.
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에 할당합니다.이제 Outer Class Object는 GC에 적합하지만 실행 중인 스레드 객체와 (Has-A) 관계를 가진 내부 클래스 객체는 여전히 존재합니다.기존 Outer class 객체가 없으면 기존 m1() 메서드가 존재하지 않으며 기존 m1() 메서드가 없으면 로컬 변수가 존재하지 않지만 Inner Class 객체가 m1() 메서드의 로컬 변수를 사용하는 경우 모든 것이 설명됩니다.
이 문제를 해결하려면 로컬 변수의 복사본을 만든 다음 내부 클래스 개체를 사용하여 힙에 복사해야 합니다. java는 실제로 변수가 아니기 때문에 최종 변수에 대해서만 상수와 같습니다(모든 것은 실행 시간이 아닌 컴파일 시에만 발생합니다).
위의 문제를 해결하기 위해 언어마다 다른 결정을 내립니다.
Java의 경우 솔루션은 이 기사에 나와 있는 바와 같습니다.
C#의 경우 솔루션은 부작용을 허용하며 참조에 의한 캡처가 유일한 옵션입니다.
C++11의 경우, 해결책은 프로그래머가 결정을 내리도록 하는 것이다.값 또는 참조로 캡처할 수 있습니다.값으로 캡처할 경우 참조되는 변수가 실제로 다르기 때문에 부작용이 발생하지 않습니다.참조로 캡처할 경우 부작용이 발생할 수 있지만 프로그래머는 이를 인식해야 합니다.
변수가 최종 변수가 아니면 익명 클래스에서 변수가 변경되지 않기 때문에 혼란스럽기 때문입니다.
변수 '가격'과 '라스트 프라이스'를 최종 결정하십시오.
--편집
아, 그리고 기능에 할당하지 않아도 됩니다.새로운 지역 변수가 필요합니다.어쨌든, 지금쯤은 누군가 당신에게 더 나은 답을 줬을 겁니다.
언급URL : https://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen
'IT이야기' 카테고리의 다른 글
C에 파일이 있는지 확인하는 가장 좋은 방법은 무엇입니까? (0) | 2022.06.11 |
---|---|
printf를 통해 보완이 다르게 동작하는 이유는 무엇입니까? (0) | 2022.06.11 |
char*와 const char*의 차이점 (0) | 2022.06.11 |
스타일링 Vue 슬롯 (0) | 2022.06.11 |
log4j: 특정 클래스의 출력을 특정 Appender에 기록합니다. (0) | 2022.06.11 |