Доступ к переменной осуществляется внутри внутреннего класса. Нужно быть объявленным окончательным

Итак, название говорит все. Я получаю ошибку компиляции внутри моего onClick.

Здесь код.

public class fieldsActivity extends Activity {

Button addSiteButton;
Button cancelButton;
Button signInButton;


/**
 * Called when the activity is first created.
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // to create a custom title bar for activity window
    requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

    setContentView(R.layout.fields);
    // use custom layout title bar
    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.topbar);

    Pager adapter = new Pager();
    ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
    mPager.setAdapter(adapter);
    mPager.setCurrentItem(1);



    addSiteButton = (Button) findViewById(R.id.addSiteButton);
    addSiteButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
           mPager.setCurrentItem(2, true); //Compilation error happens here.
        }


    });


    cancelButton = (Button) findViewById(R.id.cancel_button);
    signInButton = (Button) findViewById(R.id.sign_in_button);

}

Ответ 1

Если вы не хотите делать это окончательным, вы всегда можете просто сделать глобальную переменную.

Ответ 2

Вы можете объявить переменную final или сделать ее переменной экземпляра (или глобальной). Если вы объявите его окончательным, вы не сможете изменить его позже.

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

Я написал более подробное объяснение здесь. Он также объясняет, почему экземпляры и глобальные переменные не должны быть объявлены окончательными.

Ответ 3

Ошибка говорит все, измените:

ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);

к

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);

Ответ 4

Здесь забавный ответ.

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

Я на самом деле не могу претендовать на кредит за это. Это была рекомендация IntelliJ! Чувствует себя немного хакером. Но не так плохо, как глобальная переменная, поэтому я подумал, что стоит упомянуть здесь. Это всего лишь одно решение проблемы. Не обязательно лучший.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
       tapCount[0]++;
    }

});

Ответ 5

Как сказал @Veger, вы можете сделать его final, чтобы переменная могла использоваться во внутреннем классе.

final ViewPager pager = (ViewPager) findViewById(R.id.fieldspager);

Я назвал его pager, а не mPager, потому что вы используете его как локальную переменную в методе onCreate. Префикс m зарезервирован для переменных класса (т.е. Переменных, объявленных в начале класса и доступных для всех методов класса).

Если вам действительно нужна переменная-член класса, она не работает, чтобы сделать ее окончательной, потому что вы не можете использовать findViewById для установки ее значения до onCreate. Решение состоит в том, чтобы не использовать анонимный внутренний класс. Таким образом, переменная mPager не должна быть объявлена ​​окончательной и может использоваться во всем классе.

public class MainActivity extends AppCompatActivity {

    private ViewPager mPager;
    private Button mButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // ...

        mPager = (ViewPager) findViewById(R.id.fieldspager);

        // ...

        mButton.setOnClickListener(myButtonClickHandler);
    }


    View.OnClickListener myButtonClickHandler = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mPager.setCurrentItem(2, true);
        }
    };
}

Ответ 6

Когда вы создаете inner classAbout, фактически компилятор генерирует новый класс - <ClassName>$<Counter>.class и передает следующие параметры в конструкторLocal variable on stack

  1. ссылка на внешний класс
  2. окончательные локальные переменные, которые используются внутри

Невозможно переназначить переменную из конструктора (или метода), но просто изменить состояние передаваемой переменной. Вот почему вы можете использовать:

  1. поле внешнего класса
  2. окончательная локальная переменная ссылочного типа (Object...) будет очищена
  3. оберните локальную переменную типа значения (int...) и передайте ее как ссылочный тип (IntelliJ IDEA предлагает вам преобразовать переменную в окончательный массив из одного элемента)