Tidak dapat merujuk ke variabel non-final di dalam kelas batin yang didefinisikan dalam metode yang berbeda

247

Diedit: Saya perlu mengubah nilai beberapa variabel karena mereka menjalankan beberapa kali thorugh timer. Saya harus terus memperbarui nilai dengan setiap iterasi melalui penghitung waktu. Saya tidak dapat menetapkan nilai ke final karena itu akan mencegah saya memperbarui nilai namun saya mendapatkan kesalahan yang saya jelaskan dalam pertanyaan awal di bawah ini:

Saya sebelumnya telah menulis apa yang ada di bawah ini:

Saya mendapatkan kesalahan "tidak dapat merujuk ke variabel non-final di dalam kelas batin yang didefinisikan dalam metode yang berbeda".

Ini terjadi untuk harga yang disebut dobel dan Harga yang disebut priceObject. Apakah Anda tahu mengapa saya mendapatkan masalah ini? Saya tidak mengerti mengapa saya harus memiliki deklarasi akhir. Juga jika Anda dapat melihat apa yang saya coba lakukan, apa yang harus saya lakukan untuk mengatasi masalah ini.

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
sumber
Yang saya tanyakan adalah, bagaimana cara saya mendapatkan variabel dalam penghitung waktu yang dapat terus saya perbarui.
Ankur
1
@Ankur: jawaban sederhananya adalah "Tidak". Tetapi Anda dapat mencapai efek yang diinginkan menggunakan kelas batin; lihat jawaban @ petercardona.
Stephen C

Jawaban:

197

Java tidak mendukung penutupan yang sebenarnya , meskipun menggunakan kelas anonim seperti yang Anda gunakan di sini ( new TimerTask() { ... }) terlihat seperti semacam penutupan.

sunting - Lihat komentar di bawah ini - berikut ini bukan penjelasan yang benar, seperti yang ditunjukkan KeeperOfTheSoul.

Inilah mengapa itu tidak berhasil:

Variabel lastPricedan harga adalah variabel lokal dalam metode main (). Objek yang Anda buat dengan kelas anonim mungkin bertahan hingga setelah main()metode kembali.

Ketika main()metode kembali, variabel lokal (seperti lastPricedan price) akan dibersihkan dari tumpukan, sehingga mereka tidak akan ada lagi setelah main()kembali.

Tetapi objek kelas anonim referensi variabel-variabel ini. Hal-hal buruk akan menjadi salah jika objek kelas anonim mencoba mengakses variabel setelah mereka dibersihkan.

Dengan membuat lastPricedan price final, mereka bukan benar-benar variabel lagi, tetapi konstanta. Compiler kemudian dapat mengganti penggunaan lastPricedan pricedi kelas anonim dengan nilai-nilai konstanta (pada waktu kompilasi, tentu saja), dan Anda tidak akan memiliki masalah dengan mengakses variabel yang tidak ada lagi.

Bahasa pemrograman lain yang mendukung penutupan melakukannya dengan memperlakukan variabel-variabel tersebut secara khusus - dengan memastikan mereka tidak hancur ketika metode berakhir, sehingga penutupan masih dapat mengakses variabel.

@Ankur: Anda bisa melakukan ini:

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);      
}
Jesper
sumber
34
Tidak sepenuhnya benar, Java memang menghasilkan tangkapan untuk variabel yang dimaksud untuk menangkap nilai run-time mereka, hanya saja mereka ingin menghindari efek samping aneh yang mungkin terjadi di .Net di mana dengan Anda menangkap nilai dalam delegasi, ubah nilai dalam metode luar, dan sekarang delegasi melihat nilai baru lihat, stackoverflow.com/questions/271440/c-captured-variable-in-loop untuk contoh C # dari perilaku ini yang ingin dihindari oleh Java.
Chris Chilvers
14
Itu bukan "efek samping yang aneh", itu adalah perilaku normal yang orang harapkan - dan yang tidak bisa disampaikan oleh Java karena tidak menghasilkan tangkapan. Sebagai solusinya, variabel lokal yang digunakan dalam kelas anonim harus final.
Michael Borgwardt
12
Jesper, Anda mungkin harus mengedit bagian jawaban yang salah daripada hanya memiliki pesan yang mengatakan hal di atas tidak benar.
James McMahon
19
Java sebenarnya tidak mendukung penutupan. Bahasa yang mendukung penutupan melakukannya dengan menyimpan seluruh lingkungan lokal (yaitu, set variabel lokal yang didefinisikan dalam bingkai tumpukan saat ini) sebagai objek tumpukan. Java tidak memiliki dukungan untuk ini (perancang bahasa ingin mengimplementasikannya tetapi kehabisan waktu), sehingga sebagai solusi, setiap kali kelas lokal dipakai, nilai-nilai variabel lokal mana pun yang merujuknya disalin ke tumpukan . Namun, JVM tidak dapat kemudian menyimpan nilai-nilai dalam sinkronisasi dengan variabel lokal, itulah sebabnya mereka harus final.
Taymon
64
Jawaban ini benar-benar membingungkan sekarang karena tidak ada seorang pun yang bernama "KeeperOfTheSoul" yang berkomentar. Jawabannya harus direvisi.
Adam Parkin
32

Untuk menghindari efek samping yang aneh dengan penutupan dalam variabel java yang dirujuk oleh delegasi anonim harus ditandai sebagai final, jadi untuk merujuk lastPricedan harga dalam tugas timer mereka harus ditandai sebagai final.

Ini jelas tidak akan berfungsi untuk Anda karena Anda ingin mengubahnya, dalam hal ini Anda harus melihat merangkum mereka dalam kelas.

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;
    }
}

sekarang cukup buat Foo baru sebagai final dan panggil .tick dari timer.

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);
}
Chris Chilvers
sumber
1
atau Anda bisa mengarahkan Foo mengimplementasikan Runnable ..?
vidstige
18

Anda hanya dapat mengakses variabel akhir dari kelas yang berisi ketika menggunakan kelas anonim. Oleh karena itu Anda perlu untuk mendeklarasikan variabel yang digunakan akhir (yang bukan merupakan pilihan bagi Anda karena Anda mengubah lastPrice dan harga ), atau tidak menggunakan kelas anonim.

Jadi, pilihan Anda adalah membuat kelas dalam yang sebenarnya, di mana Anda bisa meneruskan variabel dan menggunakannya secara normal

atau:

Ada hack cepat (dan menurut saya jelek) untuk variabel LastPrice dan harga Anda yang menyatakan seperti itu

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

dan di kelas anonim Anda, Anda dapat mengatur nilai seperti ini

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

Penjelasan yang baik untuk mengapa Anda tidak dapat melakukan apa yang Anda coba lakukan sudah disediakan. Sebagai solusi, mungkin pertimbangkan:

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 );
    }
}

Sepertinya Anda bisa melakukan desain yang lebih baik dari itu, tetapi idenya adalah Anda bisa mengelompokkan variabel yang diperbarui di dalam referensi kelas yang tidak berubah.

Peter Cardona
sumber
11

Dengan kelas anonim, Anda sebenarnya mendeklarasikan kelas bersarang "tanpa nama". Untuk kelas bertingkat, kompiler menghasilkan kelas publik mandiri baru dengan konstruktor yang akan mengambil semua variabel yang digunakan sebagai argumen (untuk kelas bertingkat "bernama", ini selalu merupakan turunan dari kelas asli / tertutup). Ini dilakukan karena lingkungan runtime tidak memiliki gagasan tentang kelas bersarang, sehingga perlu ada konversi (otomatis) dari kelas bersarang ke kelas mandiri.

Ambil kode ini misalnya:

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);
    }
}

Itu tidak akan berhasil, karena inilah yang dilakukan oleh kompiler di bawah tenda:

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);
}

Kelas anonim asli digantikan oleh beberapa kelas mandiri yang dihasilkan oleh kompiler (kode tidak tepat, tetapi harus memberi Anda ide yang baik):

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

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

Seperti yang Anda lihat, kelas mandiri memegang referensi ke objek bersama, ingat bahwa semua yang ada di java adalah nilai per nilai, jadi meskipun variabel referensi 'dibagi' di EnclosingClass akan diubah, contoh yang ditunjukkannya tidak dimodifikasi. , dan semua variabel referensi lainnya yang menunjuk ke sana (seperti yang ada di kelas anonim: Melampirkan $ 1), tidak akan mengetahui hal ini. Ini adalah alasan utama kompilator memaksa Anda untuk mendeklarasikan variabel 'dibagikan' ini sebagai final, sehingga jenis perilaku ini tidak akan membuatnya menjadi kode yang sudah berjalan.

Sekarang, inilah yang terjadi ketika Anda menggunakan variabel instan di dalam kelas anonim (inilah yang harus Anda lakukan untuk menyelesaikan masalah Anda, pindahkan logika Anda ke metode "instance" atau konstruktor suatu kelas):

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);
    }
}

Ini mengkompilasi dengan baik, karena kompiler akan mengubah kode, sehingga kelas baru yang dihasilkan Menutupi $ 1 akan menyimpan referensi ke instance EnclosingClass di mana ia dipakai (ini hanya representasi, tetapi harus membuat Anda pergi):

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);
    }
}

Seperti ini, ketika variabel referensi 'dibagikan' di EnclosingClass akan dipindahkan, dan ini terjadi sebelum panggilan ke Thread # run (), Anda akan melihat "halo lainnya" dicetak dua kali, karena sekarang variabel penutup EnclosingClass $ 1 # akan menyimpan referensi ke objek kelas yang dideklarasikan, jadi perubahan pada atribut apa pun pada objek itu akan terlihat oleh instance dari EnclosingClass $ 1.

Untuk informasi lebih lanjut tentang hal ini, Anda dapat melihat posting blog yang sangat baik ini (tidak ditulis oleh saya): http://kevinboone.net/java_inner.html

emerino
sumber
Bagaimana jika variabel lokal 'dibagikan' adalah objek yang dapat diubah? Sesuai penjelasan Anda yang menyatakan 'final' juga tidak akan membantu, kan?
sactiw
Mendeklarasikan "dibagikan" sebagai final akan memungkinkan Anda untuk memodifikasi keadaan objek sebagai referensi variabel final, tetapi untuk contoh khusus ini tidak akan berfungsi karena Anda tidak akan dapat mengubah nilai variabel "shared" (yang adalah apa yang diinginkan OP), Anda akan dapat menggunakannya di dalam kelas anonim, tetapi nilainya tidak akan berubah (karena dinyatakan final). Penting untuk memperhatikan perbedaan antara variabel dan nilai aktual yang mereka pegang (yang mungkin primitif atau referensi ke objek di tumpukan).
emerino
>>> tapi nilainya tidak akan berubah. Saya kira Anda kehilangan titik yaitu jika variabel referensi akhir menunjuk ke objek yang bisa berubah, masih dapat diperbarui, namun, kelas anonim membuat salinan dangkal sehingga perubahan tercermin dalam anonim kelas. Dengan kata lain, negara dalam sinkronisasi yang merupakan apa yang diinginkan di sini. Di sini, OP membutuhkan kemampuan untuk memodifikasi variabel bersama (tipe primitif) dan untuk mencapai itu OP perlu membungkus nilai di bawah objek yang bisa berubah dan berbagi objek yang bisa diubah.
sactiw
1
Tentu saja OP dapat membungkus nilai yang dibutuhkan di bawah objek yang bisa berubah, mendeklarasikan variabel sebagai final dan menggunakannya. Namun, ia dapat menghindari menggunakan objek tambahan dengan mendeklarasikan variabel sebagai atribut dari kelas saat ini (seperti yang ditunjukkan dan dijelaskan dalam jawaban). Memaksa objek yang bisa berubah-ubah (seperti menggunakan array hanya untuk dapat memodifikasi nilai dari variabel bersama) bukanlah ide yang baik.
emerino
7

Ketika saya menemukan masalah ini, saya hanya meneruskan objek ke kelas batin melalui konstruktor. Jika saya harus melewati primitif atau objek yang tidak dapat diubah (seperti dalam kasus ini), kelas wrapper diperlukan.

Sunting: Sebenarnya, saya tidak menggunakan kelas anonim sama sekali, tetapi subkelas yang tepat:

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);
    }
Buhb
sumber
2

Anda tidak dapat merujuk ke variabel non-final karena Spesifikasi Bahasa Jawa mengatakan demikian. Dari 8.1.3:
"Setiap variabel lokal, parameter metode formal atau parameter penangan pengecualian yang digunakan tetapi tidak dideklarasikan di kelas dalam harus dinyatakan final." Paragraf utuh.
Saya hanya dapat melihat bagian dari kode Anda - menurut saya penjadwalan modifikasi variabel lokal adalah ide yang aneh. Variabel lokal tidak ada lagi ketika Anda meninggalkan fungsi. Mungkin bidang statis suatu kelas akan lebih baik?

Tadeusz Kopec
sumber
2

Saya hanya menulis sesuatu untuk menangani sesuatu sesuai dengan maksud penulis . Saya menemukan hal terbaik untuk dilakukan adalah membiarkan konstruktor mengambil semua objek dan kemudian dalam metode yang Anda terapkan gunakan objek konstruktor.

Namun, jika Anda menulis kelas antarmuka generik, maka Anda harus melewati Object, atau lebih baik daftar Object. Ini bisa dilakukan oleh Object [] atau bahkan lebih baik, Object ... karena lebih mudah untuk dipanggil.

Lihat contoh saya tepat di bawah.

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;
    }

}

Silakan lihat posting ini tentang penutupan Java yang mendukung ini di luar kotak: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html

Versi 1 mendukung lewat penutupan non-final dengan autocasting:
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));
        }
    });
mmm
sumber
2

Jika Anda ingin mengubah nilai dalam pemanggilan metode di dalam kelas anonim, "nilai" itu sebenarnya a Future. Jadi, jika Anda menggunakan Jambu Biji, Anda bisa menulis

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

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

 return myvalue.get();
Mesin Tanah
sumber
2

Salah satu solusi yang saya perhatikan tidak disebutkan (kecuali saya melewatkannya, jika saya perbaiki saya), adalah penggunaan variabel kelas. Berlari ke dalam masalah ini mencoba untuk menjalankan thread baru dalam metode: new Thread(){ Do Something }.

Panggilan doSomething()dari berikut ini akan berhasil. Anda tidak harus mendeklarasikannya final, hanya perlu mengubah ruang lingkup variabel sehingga tidak dikumpulkan sebelum innerclass. Ini kecuali jika tentu saja proses Anda sangat besar dan mengubah ruang lingkup dapat menciptakan semacam konflik. Saya tidak ingin membuat variabel saya final karena tidak final / konstan.

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();
    }

}
James
sumber
1

Jika variabel harus final, tidak bisa maka Anda dapat menetapkan nilai variabel ke variabel lain dan membuat ITU final sehingga Anda dapat menggunakannya sebagai gantinya.

Thorbjørn Ravn Andersen
sumber
1

gunakan ClassName.this.variableName untuk referensi variabel non-final

pengguna3251651
sumber
1

Anda bisa mendeklarasikan variabel di luar kelas luar. Setelah ini, Anda akan dapat mengedit variabel dari dalam kelas dalam. Saya kadang-kadang menghadapi masalah yang sama saat coding di android jadi saya mendeklarasikan variabel sebagai global dan berfungsi untuk saya.

Abhijay Ghildyal
sumber
Ini tidak benar-benar menjawab pertanyaan ... Itu sebabnya Anda mendapatkan downvoted.
Stuart Siegler
0

Bisakah Anda membuat lastPrice,, priceObjectdan pricebidang kelas dalam anonim?

Greg Mattes
sumber
0

Perhatian utama adalah apakah variabel di dalam instance kelas anonim dapat diselesaikan pada saat run-time. Bukan suatu keharusan untuk membuat variabel final selama dijamin bahwa variabel berada di dalam lingkup run-time. Sebagai contoh, silakan lihat dua variabel _statusMessage dan _statusTextView di dalam metode updateStatus ().

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);

                }

            });
        }
    }

}
WaiKit Kung
sumber
0

Apa yang berhasil bagi saya hanyalah mendefinisikan variabel di luar fungsi ini dari Anda.

Tepat sebelum fungsi utama menyatakan yaitu

Double price;
public static void main(String []args(){
--------
--------
}
punkck
sumber
Itu tidak akan berfungsi, Anda mendeklarasikan variabel instance, untuk menggunakannya, Anda harus membuat instance di dalam metode utama Anda. Anda harus lebih spesifik atau cukup tambahkan pengubah statis ke variabel 'harga'.
emerino
0

Deklarasikan variabel sebagai statis dan referensi dalam metode yang diperlukan menggunakan className.variable

shweta
sumber
Non-static parameter cannot be referenced from a static context
stephen
@Shweta variabel lokal dan parameter metode tidak dapat dideklarasikan 'statis', apalagi, ini tentang cara itu telah diterapkan untuk memungkinkan kelas dalam metode (kelas anonim lokal) untuk melanjutkan akses ke variabel lokal dan parameter metode bahkan setelah metode telah kembali yaitu membuat salinan 'final' dan menggunakannya sebagai variabel instan.
sactiw
0

Hanya penjelasan lain. Perhatikan contoh di bawah ini

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");
    }
}

Di sini Output akan

m1 Selesai

Utas t running

Utas t running

Utas t running

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

Sekarang metode m1 () selesai dan kami menetapkan variabel referensi o ke nol, Sekarang Obyek Kelas Luar memenuhi syarat untuk GC tetapi Objek Kelas Batin masih ada yang memiliki (Has-A) hubungan dengan objek Thread yang sedang berjalan. Tanpa objek kelas luar yang ada tidak ada kemungkinan metode m1 () yang ada dan tanpa metode m1 () yang ada tidak ada peluang ada variabel lokalnya tetapi jika objek kelas batin menggunakan variabel lokal metode m1 () maka semuanya jelas. .

Untuk menyelesaikan ini kita harus membuat salinan variabel lokal dan kemudian harus menyalin kemudian ke heap dengan objek kelas batin, apa yang dilakukan java hanya untuk variabel akhir karena mereka sebenarnya bukan variabel mereka seperti konstanta (Semuanya terjadi pada waktu kompilasi saja tidak saat runtime).

pks
sumber
-1

Untuk mengatasi masalah di atas, bahasa yang berbeda membuat keputusan yang berbeda.

untuk Java, solusinya adalah seperti yang kita lihat di artikel ini.

untuk C #, solusinya adalah memungkinkan efek samping dan menangkap dengan referensi adalah satu-satunya pilihan.

untuk C ++ 11, solusinya adalah memungkinkan programmer membuat keputusan. Mereka dapat memilih untuk menangkap berdasarkan nilai atau referensi. Jika menangkap berdasarkan nilai, tidak ada efek samping yang akan terjadi karena variabel yang dirujuk sebenarnya berbeda. Jika ditangkap dengan referensi, efek samping dapat terjadi tetapi programmer harus menyadarinya.

Mesin Tanah
sumber
-2

Karena membingungkan jika variabelnya tidak final, karena perubahan itu tidak akan diambil di kelas anonim.

Jadikan variabel sebagai 'harga' dan 'harga terakhir'.

- Edit

Ups, dan Anda juga harus tidak menetapkannya, jelas, dalam fungsi Anda. Anda akan memerlukan variabel lokal baru. Bagaimanapun, saya menduga seseorang telah memberi Anda jawaban yang lebih baik sekarang.

Sutra Siang
sumber
2
itu tidak hanya membingungkan - itu benar-benar salah, sehingga kompiler tidak mengizinkannya.
Chii
Tetapi kemudian bagaimana saya mengubah nilai-nilai itu ketika saya perlu?
Ankur
Bukan hanya karena membingungkan; ini karena Java tidak mendukung penutupan. Lihat jawaban saya di bawah ini. @Ankur: Anda bisa membuat variabel variabel anggota objek kelas anonim alih-alih variabel lokal di main ().
Jesper
Dia memodifikasi mereka, sehingga mereka tidak bisa final.
Robin
Jika harga dan Harga terakhir adalah final, penugasan kepada mereka tidak akan dikompilasi.
Greg Mattes