2

Hi im trying to add calculations from my database depending on the user request but my float will not work unless its final, how can I get around this?

Code:

float v = (float) 0;
    StringBuilder upcoming = new StringBuilder("Upcoming\n\n");
    StringBuilder Results = new StringBuilder("Upcoming\n\n");
    switchExam.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if(!isChecked)
            {
                for(FAssessment a : assessmentList) {
                    if(!a.getType().equals("Final"))
                    {
                        double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                        v += (float) d;
                    }                        
                }
            }
            else
            {
                for(FAssessment a : assessmentList) {
                    double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                    v += (float) d;
                }
            }
        }
    });

This is where I'm getting the error v += (float) d;

Variable 'v' is accessed from within inner class, needs to be final or effectively final

Glenn
  • 12,741
  • 6
  • 47
  • 48
Erbez
  • 321
  • 5
  • 17
  • 4
    You are creating an anonymous class to be used as a `OnCheckedChangeListener`. That event con happen to the `switchExam` object in the future, well after the current function has finished (I mean the function where `float v` is scoped to), so where would the new value be stored? Since that variable will no longer be available, it makes no sense to store a reference to it. See https://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class – frozenkoi Oct 02 '18 at 23:48

2 Answers2

2

Maybe reverse who keeps what?

StringBuilder upcoming = new StringBuilder("Upcoming\n\n");
StringBuilder Results = new StringBuilder("Upcoming\n\n");

CompoundButton.OnCheckedChangeListener lister = new CompoundButton.OnCheckedChangeListener() {

    float v = 0.0;
    public float getV() { return v; }

    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(!isChecked)
        {
            for(FAssessment a : assessmentList) {
                if(!a.getType().equals("Final"))
                {
                    double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                    v += (float) d;
                }                        
            }
        }
        else
        {
            for(FAssessment a : assessmentList) {
                double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                v += (float) d;
            }
        }
    }
};
switchExam.setOnCheckedChangeListener(lister);

then when the enclosing program needs to know what v is it can call

lister.getV();

NB: instead of casting 0 to a float, you might also be able to say something like:

float v = 0f;
Rick
  • 576
  • 3
  • 12
  • 1
    [Another](https://stackoverflow.com/questions/14425826/variable-is-accessed-within-inner-class-needs-to-be-declared-final) way to put it: "Reason: if two methods see the same local variable, Java wants you to swear you will not change it - final, in Java speak. Together with the absence of by-reference parameters, this rule ensures that locals are only assigned in the method that they belong to..." – paulsm4 Oct 02 '18 at 23:58
  • Thank you very much for the input, I tried it but it didn't work – Erbez Oct 03 '18 at 12:27
2

From what I understand, you want to use this variable v again later. If so, you can take the code you need and put it in a method. That way you can declare the variable in the listener, which would give it the final it is looking for.

Like this:

switchExam.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            StringBuilder upcoming = new StringBuilder("Upcoming\n\n");
            StringBuilder Results = new StringBuilder("Results\n\n");
            float v= 0;
            if(!isChecked)
            {

                for(FAssessment a : assessmentList) {
                    if(!a.getType().equals("Final"))
                    {
                        double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                        v += (float) d;
                    }

                    upcoming.append(a.getDesc()+" "+a.getDate()+"\n");
                    Results.append(a.getDesc()+" "+format.format(((a.getAchieved()/a.getTotal())*100))+"%\n");
                }                    
                SetInit(v,upcoming,Results);
            }
            else
            {
                for(FAssessment a : assessmentList) {
                    double d = CalculatePercentage(a.getTotal(),a.getAchieved(),Double.parseDouble(a.getWeight()));
                    v += (float) d;
                    upcoming.append(a.getDesc()+" "+a.getDate()+"\n");
                    Results.append(a.getDesc()+" "+format.format(((a.getAchieved()/a.getTotal())*100))+"%\n");
                }
                SetInit(v,upcoming,Results);

            }
        }
    });
    switchExam.setChecked(true);

Then in your method, you would paste what you want to do with your variable v:

private void SetInit(float v, StringBuilder coming, StringBuilder results) {
    txtUpcoming.setText(coming);
    txtResults.setText(results);
    //And whatever you want to do with `v`
 }

Hope this helps. :)

Cleaven
  • 974
  • 2
  • 9
  • 30