Невозможно назначить локальную переменную, потому что она определена вне

Я пишу статический метод, и у меня возникают проблемы с доступом к переменной из внутреннего метода. В Eclipse появляется следующая ошибка:

Последняя локальная переменная ret не может быть назначена, так как она определена во включающем типе

Вот мой код:

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;
}

person smartmouse    schedule 18.11.2014    source источник
comment
попробуйте его только с объявлением: (т.е. final boolean ret = false;) и не устанавливайте его во внутренних методах   -  person Martin    schedule 18.11.2014
comment
Я получаю эту ошибку на ret = false;, а не на final boolean ret;   -  person smartmouse    schedule 18.11.2014
comment
нет. см. мой ответ ниже   -  person Martin    schedule 19.11.2014


Ответы (3)


Дело в том, что локальные переменные метода из включающего типа фактически копируются в экземпляры анонимных классов (это из-за проблем с кадром активации, но я не буду вдаваться в подробности, поскольку это на самом деле не имеет отношения к вопросу), что вот почему они должны быть окончательными, потому что переменная в экземпляре вложенного типа больше не та.

Итак, вот первый пример:

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

Это не компилируется, потому что вы не можете ссылаться на неконечную переменную в методе анонимного класса. Когда вы добавляете модификатор final к объявлению a, значение a будет скопировано в созданный экземпляр анонимного класса, который вы определили. Однако вы не сможете изменить значение a, потому что изменения не будут видны для метода, в котором был объявлен a.

Однако анонимные классы не являются статическими, то есть у них есть ссылка на включающий экземпляр (если метод, в котором они объявлены, не является статическим), который вы можете использовать для изменения переменных включающего экземпляра:

int a = 3;

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

Этот пример компилируется, и он будет увеличивать a на 3 каждый раз, когда вызывается метод run () экземпляра анонимного класса. (В этом примере он никогда не вызывается, но это всего лишь пример.)

Итак, чтобы подвести итог, вам необходимо преобразовать переменную seatno из локальной переменной метода в переменную экземпляра включающего типа. Или, если это еще не так, вам нужно удалить модификатор final, поскольку переменные final могут быть назначены только один раз.

Обновление: в Java 8 введена концепция эффективных конечных переменных (см. Спецификацию языка Java). Однако в первом примере этого поста переменная a назначается несколько раз, что не позволяет ей быть фактически окончательной. Это означает, что этот пример по-прежнему не компилируется с Java 8. (Ошибка компиляции: «Локальная переменная a, определенная во включающей области, должна быть окончательной или фактически окончательной»)

person Prasad    schedule 18.11.2014
comment
Я добавляю boolean ret = false; в начале класса (перед методом), но получаю следующую ошибку: Cannot make a static reference to the non-static field ret - person smartmouse; 18.11.2014

Вы смешиваете синхронный и асинхронный код. Ваш логический метод выполнит весь код и вернется, прежде чем вы покажете свой диалог. Вместо этого вам нужно либо предоставить обратный вызов, либо вызвать метод внутри прослушивателей кликов. Что-то вроде этого...

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
}

Вам нужно будет сделать то же самое и для setPositiveButton(). См. Другой пример в этом вопросах.

person kevskree    schedule 18.11.2014
comment
Я не могу использовать это решение, мне просто нужно проверить переменную ret. - person smartmouse; 18.11.2014
comment
Вы создаете свой код непригодным для использования способом. Метод выполнит весь код и вернет false, прежде чем пользователь сможет нажать кнопку в диалоговом окне. Единственный способ получить значение при отображении асинхронного диалога - это использовать обратные вызовы или методы вызова внутри прослушивателей кликов. Документы Android очень хорошо объясняют, как показывать диалоги. и отвечать на вводимые пользователем данные. - person kevskree; 18.11.2014

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();

если (! ret) вернет истину; иначе вернет ложь; }

поскольку вы устанавливаете только значение false, вам не нужно его менять.

person Martin    schedule 19.11.2014