Неможливо посилатися на незакінчену змінну всередині внутрішнього класу, визначену іншим методом


247

Відредаговано: мені потрібно змінити значення декількох змінних, оскільки вони працюють кілька разів за допомогою таймера. Мені потрібно постійно оновлювати значення з кожною ітерацією через таймер. Я не можу встановити значення остаточними, оскільки це не дозволить мені оновити значення, однак я отримую помилку, яку я описую в початковому запитанні нижче:

Я раніше писав, що нижче:

Я отримую помилку "не можу посилатися на незакінчену змінну всередині внутрішнього класу, визначену іншим методом".

Це відбувається для подвійної, що називається ціною, і Ціни, званої 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);
}

Що я запитую - це як отримати змінну в таймері, яку я можу постійно оновлювати.
Анкур

1
@Ankur: проста відповідь - "Ні". Але досягти бажаного ефекту можна за допомогою внутрішнього класу; див. відповідь @ petercardona.
Стівен C

Відповіді:


197

Java не підтримує справжні закриття , навіть якщо використання анонімного класу, як ви використовуєте тут ( new TimerTask() { ... }), виглядає як своєрідне закриття.

редагувати - Дивіться коментарі нижче - наступне не є правильним поясненням, як вказує KeeperOfTheSoul.

Ось чому це не працює:

Змінні lastPriceта ціна є локальними змінними в методі main (). Об'єкт, який ви створюєте з анонімним класом, може тривати до 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);      
}

34
Не зовсім вірно, Java дійсно генерує захоплення відповідними змінними, щоб захопити їх значення часу виконання, просто вони хотіли уникнути дивного побічного ефекту, який можливий у. Ні, де, захопивши значення у делегата, змінити значення у зовнішньому методі, і тепер делегат бачить нове значення див., stackoverflow.com/questions/271440/c-captured-variable-in-loop для прикладу C # такої поведінки, якого Java прагне уникнути.
Кріс Чілверс

14
Це не "дивний побічний ефект", це нормальна поведінка, яку люди очікували - і яку Java не може доставити оскільки вона не генерує захоплення. Як вирішення, локальні змінні, що використовуються в анонімному класі, повинні бути остаточними.
Майкл Боргвардт

12
Джеспер, ти, мабуть, повинен редагувати неправильні частини своїх відповідей, а не мати повідомлення про те, що сказане вище є невірним.
Джеймс Макмахон

19
Насправді Java не підтримує закриття. Мови, що підтримують закриття, роблять це, зберігаючи все локальне середовище (тобто набір локальних змінних, визначених у поточному кадрі стека), як об’єкт купи. У Java немає підтримки для цього (мовні дизайнери хотіли реалізувати це, але не вистачало часу), тому як вирішення, коли локальний клас інстанціюється, значення будь-яких локальних змінних, на які він посилається, копіюються в купу . Однак JVM не може потім зберігати значення синхронізовано з локальними змінними, тому вони повинні бути остаточними.
Таймон

64
Ця відповідь повністю збиває з пантелику, коли ніхто не назвав "KeeperOfTheSoul", хто б це не прокоментував. Відповідь слід переглянути.
Адам Паркін

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

Ви можете отримати доступ до кінцевих змінних із класу, що містить, якщо використовувати анонімний клас. Тому вам потрібно оголосити використовувані змінні значення остаточними (що для вас не є варіантом, оскільки ви змінюєте 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 є прохідним значенням, тому навіть якщо змінна посилання 'shared' у EnclosingClass змінюється, екземпляр, на який вона вказує, не змінюється , і всі інші змінні посилання, що вказують на нього (як, наприклад, в анонімному класі: Приєднання $ 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);
    }
}

Так, коли довідкова змінна 'shared' в EnclosingClass перепризначається, і це відбувається перед викликом до Thread # run (), ви побачите "інший привіт", надрукований двічі, тому що тепер EnclosingClass $ 1 # змінна, що закриває, буде зберігати посилання до об’єкта класу, де він був оголошений, тому зміни будь-якого атрибуту на цьому об'єкті будуть видимі для примірників EnclosingClass $ 1.

Для отримання додаткової інформації з цього питання ви можете ознайомитись із цим чудовим повідомленням у блозі (не написаним мною): http://kevinboone.net/java_inner.html


Що робити, якщо локальна змінна 'shared' є об'єктом, що змінюється? Згідно з вашим поясненням, оголошення «остаточного» теж не допоможе, правда?
Сактів

Оголошення "спільного" як остаточне дозволить вам змінити стан об'єкта кінцевими посиланнями змінної, але для цього конкретного прикладу він не працюватиме, оскільки ви не зможете змінити значення змінної "спільного користування" (яка те, що хотіла ОП), ви зможете використовувати його всередині анонімних класів, але значення не зміниться (оскільки воно оголошено остаточним). Важливо помітити різницю між змінними та фактичними значеннями, які вони містять (це можуть бути примітиви або посилання на об'єкти в купі).
Емеріно

>>> але це значення не зміниться, я думаю, вам не вистачає точки, тобто якщо остаточна контрольна змінна вказує на об'єкт, що змінюється, він все одно може бути оновлений, однак, анонімний клас створює дрібну копію, таким чином зміни відображаються в анонімному клас. Іншими словами, стан синхронізується, що тут бажано. Тут ОП потребує можливості модифікувати загальну змінну (примітивний тип) і для досягнення цього ОП потрібно обернути значення під об'єктом, що змінюється, і поділити цей об'єкт, що змінюється.
Сактів

1
Звичайно, ОП може зафіксувати необхідне значення під об'єктом, що змінюється, оголосити змінну як остаточну і використовувати це замість цього. Однак він може уникнути використання додаткового об'єкта, оголосивши змінну атрибутом поточного класу (як зазначено та пояснено у відповіді). Примушування змінних об'єктів (наприклад, використання масивів просто для того, щоб змінити значення спільної змінної) - не дуже гарна ідея.
Емеріно

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:
"Будь-яка локальна змінна, параметр формального методу або параметр обробника винятків, який використовується, але не оголошений у внутрішньому класі, повинен бути оголошений остаточним." Цілий абзац.
Я бачу лише частину вашого коду - згідно зі мною модифікація планування локальних змінних - це дивна ідея. Локальні змінні перестають існувати, коли ви виходите з функції. Можливо, статичні поля класу будуть краще?


2

Я просто написав щось, щоб обробити щось уздовж намірів авторів . Я вважав, що найкраще зробити це дозволити конструктору взяти всі об'єкти, а потім у реалізованому методі використовувати ці об’єкти конструктора.

Однак, якщо ви пишете загальний клас інтерфейсу, вам доведеться передати Об'єкт, а краще список об'єктів. Це можна зробити за допомогою 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/ Закриття.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

Якщо ви хочете змінити значення у виклику методу в анонімному класі, це "значення" є фактично a Future. Отже, якщо ви використовуєте Guava, можете писати

...
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

Якщо змінна, необхідна для остаточного, не може бути, то ви можете призначити значення змінної іншій змінній і зробити ТОТО остаточним, щоб ви могли використовувати її замість.


1

використовуйте ClassName.this.variableName для посилання на не остаточну змінну


1

ви можете просто оголосити змінну за межами зовнішнього класу. Після цього ви зможете редагувати змінну з внутрішнього класу. Я іноді стикаюся з подібними проблемами під час кодування в android, тому я оголошую цю змінну як глобальну, і вона працює для мене.


Це насправді не дає відповіді на питання ... Тому, чому ви ставитеся до відповідальності.
Стюарт Зіглер


0

Основне занепокоєння полягає в тому, чи може бути вирішена змінна всередині екземпляра анонімного класу під час виконання. Не обов'язково робити змінну остаточною, якщо гарантується, що змінна знаходиться в межах часу виконання. Наприклад, перегляньте дві змінні _statusMessage та _statusTextView всередині методу 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);

                }

            });
        }
    }

}

0

що для мене працювало - це просто визначити змінну поза цією функцією вашої.

Незадовго до того, як основну функцію оголосити, тобто

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

Це не спрацює. Ви оголошуєте змінну екземпляра, щоб використовувати її, вам потрібно створити екземпляр всередині вашого основного методу. Вам слід або бути більш конкретним, або просто додати статичний модифікатор до змінної 'price'.
emerino

0

Заявіть змінну як статичну і посилайте її на необхідний метод, використовуючи className.variable


Non-static parameter cannot be referenced from a static context
Стефан

@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 Завершує

Нитка т працює

Нитка т працює

Нитка т працює

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

Тепер метод m1 () завершується і ми присвоюємо контрольну змінну o нульовою. Тепер Об'єкт зовнішнього класу є правом на GC, але все ще існує об'єкт Внутрішнього класу, який має (Has-A) зв'язок з запущеним об'єктом Thread. Без існуючого об'єкта зовнішнього класу немає шансів існуючого методу m1 (), а без існуючого методу m1 () немає шансів на існування його локальної змінної, але якщо об'єкт Inner Class використовує локальну змінну методу m1 (), то все зрозуміло .

Щоб вирішити це, нам потрібно створити копію локальної змінної, а потім скопіювати її в купу з об'єктом Inner class, що java робить лише для остаточної змінної, оскільки вони насправді не є змінною, вони схожі на константи (Все відбувається лише в час компіляції) не під час виконання).


-1

Щоб вирішити проблему вище, різні мови приймають різні рішення.

Для Java це рішення - це те, що ми бачимо в цій статті.

для C # рішення дозволяє дозволити побічні ефекти, а зйомка посиланням - єдиний варіант.

для C ++ 11 рішення - дозволити програмісту прийняти рішення. Вони можуть вибрати захоплення за значенням або за посиланням. Якщо зафіксувати за значенням, жодних побічних ефектів не буде, оскільки змінна посилання насправді інша. Якщо зйомка за посиланням може виникнути побічні ефекти, але програміст повинен це усвідомити.


-2

Оскільки це заплутано, якщо змінна не остаточна, оскільки зміни до неї не будуть підбиратися в анонімному класі.

Просто зробіть змінні "ціна" та "остання ціна" остаточними.

- Редагувати

На жаль, вам також потрібно буде не призначати їх у своїй функції. Вам знадобляться нові локальні змінні. У будь-якому випадку, я підозрюю, що хтось вже дав вам кращу відповідь.


2
це не просто заплутано - його прямо невірно, тому компілятор не дозволяє цього.
Chii

Але тоді як я можу змінити значення, коли мені потрібно?
Анкур

Не лише тому, що це заплутано; це тому, що Java не підтримує закриття. Дивіться мою відповідь нижче. @Ankur: Ви можете зробити змінними члена змінного об'єкта анонімного класу замість локальних змінних у main ().
Джеспер

Він їх модифікує, тому вони не можуть бути остаточними.
Робін

Якщо ціна і остання ціна були остаточними, завдання на них не складатимуться.
Грег Маттес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.