Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local variable defined in an enclosing scope must be final or effectively final

Tags:

java

I'm trying to print out the percentage of x/mol, but I cannot seem to get it to work. I am getting a this error: Local variable x defined in an enclosing scope must be final or effectively final

It says this happens at line 22, and there is no easy fix for it. What is java's scope, and how can I add x to the scope so that my timer can read it.

import java.math.BigInteger;
import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;

public class mol {
    public static void main(String[] args) {

        String writable = "";
        BigInteger x = new BigInteger("0");
        BigInteger mol = new BigInteger("602214179000000000000000");
        File file = new File("molA.txt");
        BufferedWriter bw = null;
        FileWriter fw = null;
        Timer t;
        t = new Timer(10000, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println(x.divide(mol) + "%");
                System.out.println(x);
            }
        });

        System.out.println("Starting...");
        t.start();


        do {
             writable += "a";
            x = x.add(new BigInteger("1"));
        } while (x.compareTo(mol) < 0);

        try {

            fw = new FileWriter(file);
            bw = new BufferedWriter(fw);
            bw.write(writable);

            System.out.println("Done");

        } catch (IOException e) {

            System.out.println(e);

        } finally {

            try {

                if (bw != null)
                    bw.close();

                if (fw != null)
                    fw.close();

            } catch (IOException ex) {

                System.out.println(ex);

            }
        }
        t.stop();
        System.out.println("Finished!");

    }
}
like image 730
Donovan Cunningham Avatar asked Apr 03 '17 20:04

Donovan Cunningham


3 Answers

You can't use a local variable inside a nested class since the JVM requires it to be final or at least "effectively final" (which means the value of x cannot be modified down the road).

You can by-pass it by declaring x outside of main as a static variable:

static volatile BigInteger x = new BigInteger("0");
public static void main(String[] args) {
....

Pay attention that it is also declared to be volatile since the main thread modified it and you want the Timer to see the updated value of x (if you don't declare it volatile the Timer might see stale values).

like image 134
Nir Alfasi Avatar answered Sep 19 '22 11:09

Nir Alfasi


In order to use a method's local variable inside a nested class, the variable must either be either final or "effectively final", where the latter means that the compiler can prove that that the variable's value will not change during the entire execution of the method. If that condition were not satisfied then the semantics of the nested class's usage of the variable would be unclear -- which of the multiple values of the variable would be used, at which point?

There are several ways you could address the problem:

  1. For your particular purpose, it looks like you should declare x as a class variable instead of a local variable. (It cannot be an instance variable because you are accessing it from static context.) You do, however, have the alternative to

  2. make x final or effectively final. For example, copy the reference to x to a final variable, and make your ActionListener access that copy instead of accessing x.

  3. Or you could create and instantiate a named ActionListener implementation class, nested or top-level, with a constructor to which you could pass x. That implementation can then use the reference it is initialized with to access the object to which x refers, but since BigIntegers are immutable, it could not modify that object.

like image 31
John Bollinger Avatar answered Sep 20 '22 11:09

John Bollinger


Create a class to hold the BigDecimals

public class MyContainer {
    private BigDecimal x = null;

    public MyContainer(BigDecimal x) {
        this.x = x;
    }

    public BigDecimal getX() {
        return x;
    }

    public void setX(BigDecimal x) {
        this.x = x;
    }
}

Then in your code on line 14 or so.

final MyContainer myContainer = new MyContainer(new BigDecimal(0));

Then in your timer,

System.out.println(myContainer.getX().divide(mol) + "%");
like image 45
bcr666 Avatar answered Sep 22 '22 11:09

bcr666