Can't assign local variable because it is defined in outside

1.1k Views Asked by At

i'm writing a static method and i'm experiencing problems with accessing variable from inner method. I'm getting the following error in Eclipse:

The final local variable ret cannot be assigned, since it is defined in an enclosing type

Here is my code:

public static boolean noInternetAlertDialog(Context ctx) {
    final boolean ret;

    AlertDialog.Builder builder;
    builder = new AlertDialog.Builder(ctx);
    builder.setCancelable(false);
    builder.setTitle("Error");
    builder.setMessage("Connection error");

    builder.setPositiveButton("Retry", new DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog, int which)
        {
            dialog.dismiss();
            ret = false;
        }
    });

    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
            ret = false;
        }
    });         
    AlertDialog dialog = builder.create();
    dialog.show();

   if (ret)
       return true;
   else
       return false;
}
3

There are 3 best solutions below

1
On

The point is that method-local variables from the enclosing type are actually copied to instances of anonymous classes (this is because of activation frame issues, but I won't go further into detail as this is not really relevant to the question), which is why they need to be final, because the variable in the nested type instance is not the same anymore.

So, here is the first example:

void foo() {
    int a = 3;
    new Runnable() {
        @Override
        public void run() {
            a += 3;
        }
    };
}

This does not compile, because you cannot reference a non-final variable in an anonymous class' method. When you add a final modifier to the declaration of a, the value of a would be copied into the created instance of the anonymous class you have defined. However, you will not be allowed to change the value of a, because the changes would not be visible to the method where a was declared.

However, anonymous classes are not static, that is, they have a reference to the enclosing instance (unless the method where they are declared is static) which you can use to modify variables of the enclosing instance:

int a = 3;

    void foo() {
        new Runnable() {
            @Override
            public void run() {
                a += 3;
            }
        };
    }

This example does compile and it would increase a by 3 every time the run() method of the anonymous class' instance is called. (In this example it is never called, but it is just an example.)

So, to summarize, you need to convert the variable seatno from a method-local variable to an instance variable of the enclosing type. Or, if it is yet, you need to remove the final modifier as final variables can only be assigned once.

Update: In Java 8, the concept of effectively final variables is introduced (see Java Language Specification). However, in the first example of this post, the variable a is assigned multiple times, which prevents it from being effectively final. This means that this example still does not compile with Java 8. (The compile error is "Local variable a defined in an enclosing scope must be final or effectively final")

2
On

You are mixing synchronous and asynchronous code. Your boolean method will execute all of the code and return before you show your dialog. Instead you either need to provide a callback or call a method inside of the click listeners. Something like this...

public static void showNoInternetDialog(Context context) {
//setup here.
    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {

            userTappedCancel();
        }
    });  

}

private static void userTappedCancel()
{
   //Do whatever you want here
}

You'll need to do the same for the setPositiveButton() as well. See this questions for a different example.

0
On
public static boolean noInternetAlertDialog(Context ctx) {
final boolean ret = false;   //<+======== this

AlertDialog.Builder builder;
builder = new AlertDialog.Builder(ctx);
builder.setCancelable(false);
builder.setTitle("Error");
builder.setMessage("Connection error");

builder.setPositiveButton("Retry", new DialogInterface.OnClickListener(){
    @Override
    public void onClick(DialogInterface dialog, int which)
    {
        dialog.dismiss();
        // =====>    you don't need this - ret = false;
    }
});

builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
        // =====>    you don't need this - ret = false;
    }
});         
AlertDialog dialog = builder.create();
dialog.show();

if (!ret) return true; else return false; }

since you only set it to false, you don't need to change it.