java: local variables referenced from an inner class must be final or effectively final

Рейтинг: -1Ответов: 1Опубликовано: 03.06.2023

Я пытаюсь реализовать будильник, который будет работать с несколькими запросами одновременно.

Я сделал JTabbledPane, и при создании каждого JPanel попытался сделать инициализацию отдельного таймера, после которого воспроизведётся звук и Диалоговое окно с уведомлением о будильнике.

Но в Timer у меня выдает ошибку:

responseTime.(java: local variables referenced from an inner class must be final or effectively final).

Как от этого избавиться и так, что бы при создании каждого нового TabbledPane был создан аналогичный работающий таймер я не знаю. Ниже этих слов в условиях if у меня подчеркивает все responseTime красным, и как раз они выдают ошибку:

Timer buff = new Timer(1000, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (responseTime == null) return;
                        if (responseTime.before(new GregorianCalendar())) {
                            responseTime = null;
                            playSoundFromResourse("ding.wav");
                            JOptionPane.showMessageDialog(MainFrame.this, "ALARM", "ALARM", JOptionPane.INFORMATION_MESSAGE);
                        }
                    }
                });

Я бы хотел понять, как сделать все в таком виде, не меняя способ JTabbledPane. Возможно, проблема не только в этом, но и в потоках, в которых я еще не разобрался.

После картинки следуют полный код отрезка, где был реализован сам JTabbledPane.

введите сюда описание изображения

        JPanel bottomPanel = new JPanel();
        bottomPanel.setBackground(Color.BLACK);
        bottomPanel.setLayout(new BorderLayout());
        bottomPanel.setVisible(true);

        Font font2 = new Font("Verdana", Font.PLAIN, 10);
        final JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.setFont(font2);

        JPanel buttons = new JPanel();
        buttons.setBackground(Color.BLACK);
        bottomPanel.add(buttons, BorderLayout.NORTH);

        JButton add = new JButton("Add Alarm");
        add.setForeground(Color.GREEN);
        add.setBackground(Color.BLACK);
        add.setFont(font2);
        add.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                GregorianCalendar responseTime;
                JPanel buffPanel = new JPanel();
                buffPanel.setBackground(Color.BLACK);

                JTextField alarmHoursField = new JTextField();
                JTextField alarmMinutsField = new JTextField();
                alarmHoursField.setBackground(Color.GRAY);
                alarmHoursField.setBorder(new EmptyBorder(10, 50, 10, 50));
                alarmHoursField.setForeground(Color.YELLOW);
                alarmMinutsField.setForeground(Color.YELLOW);
                alarmMinutsField.setBorder(new EmptyBorder(10, 50, 10, 50));
                alarmMinutsField.setBackground(Color.GRAY);

                JLabel information = new JLabel("Write Hours and Minutes");
                information.setForeground(Color.YELLOW);
                information.setBackground(Color.BLACK);

                JButton set = new JButton("SET");
                set.setForeground(Color.GREEN);
                set.setBackground(Color.BLACK);
                responseTime = new GregorianCalendar();

                set.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        hoursAlarm = parseInt(alarmHoursField.getText());
                        minuteAlarm = parseInt(alarmMinutsField.getText());

                        responseTime.set(Calendar.HOUR_OF_DAY, hoursAlarm);
                        responseTime.set(Calendar.MINUTE, minuteAlarm);
                        responseTime.set(Calendar.SECOND, 0);

                        if (responseTime.before(new GregorianCalendar())) {
                            responseTime.add(Calendar.DAY_OF_MONTH, 1);
                        }
                    }
                });
                Timer buff = new Timer(1000, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (responseTime == null) return;
                        if (responseTime.before(new GregorianCalendar())) {
                            responseTime = null;
                            playSoundFromResourse("ding.wav");
                            JOptionPane.showMessageDialog(MainFrame.this, "ALARM", "ALARM", JOptionPane.INFORMATION_MESSAGE);
                        }
                    }
                });
                buffPanel.add(information);
                buffPanel.add(alarmHoursField);
                buffPanel.add(alarmMinutsField);
                buffPanel.add(set);
                tabbedPane.addTab("Alarm " + i++, buffPanel);

            }
        });
        buttons.add(add);

        JButton remove = new JButton("Delete Alarm");
        remove.setFont(font2);
        remove.setBackground(Color.BLACK);
        remove.setForeground(Color.RED);
        remove.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int select = tabbedPane.getSelectedIndex();
                if (select >= 0) {
                    tabbedPane.removeTabAt(select);
                    i--;
                }
            }
        });
        buttons.add(remove);
        bottomPanel.add(tabbedPane, BorderLayout.CENTER);

        //add in content panel
        bottomPanel.add(buttons, BorderLayout.NORTH);

Ответы

▲ 0

Данная ошибка компилятора указывает, что при использовании локальной переменной (в данном случае responseTime) внутри вложенного класса значение такой переменной не может быть переприсвоено. Однако, внутреннее состояние может быть изменено при помощи сеттеров.

В целом, основная проблема состоит в том, что значение responseTime требуется сбросить в null, чтобы звуковой файл был проигран только один раз. Того же самого эффекта можно достичь, если просто прекратить действие таймера, вызвав его метод stop:

Timer buff = new Timer(1000, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (responseTime.before(new GregorianCalendar())) {
            ((Timer) e.getSource()).stop(); // сразу же остановить таймер
            playSoundFromResourse("ding.wav");
            JOptionPane.showMessageDialog(MainFrame.this, "ALARM", "ALARM", JOptionPane.INFORMATION_MESSAGE);
        }
    }
});

buff.stop() вызвать нельзя, так как инициализация переменной buff ещё не завершена.

Также в представленном коде следует вызвать метод start для экземпляра таймера, иначе он не запустится.