До змінної доступний внутрішній клас. Потрібно оголосити остаточним


116

Тож заголовок каже все це. Я отримую помилку компіляції всередині свого 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
Якщо ви використовуєте Eclipse, тоді ви можете натиснути Ctrl-1 (Cmd-1 на OS X) з помилкою, вибраною для перегляду швидкого виправлення, яке покаже вам, що потрібно змінити. Детальніше тут: depth-first.com/articles/2008/01/11 / ...
Intrications

Відповіді:


130

Якщо ви не хочете зробити його остаточним, ви завжди можете просто зробити його глобальною змінною.


1
@KevinZhao Чи глобальні змінні остаточні після їх ініціалізації?
the_prole

@the_prole Я думаю, що ви можете використовувати final на Java, але я не впевнений, чи можете ви використовувати його під час створення додатка для Android, тому Googling це може бути хорошою ідеєю :-)
Кевін Чжао

15
Зрештою, використання глобальних змінних є поганою ідеєю, якщо їх можна уникнути, якщо ви не початківець, і в цьому випадку надмірне ускладнення вашої програми з глобальними - це хороший досвід навчання. Ось гарна стаття, яка пояснює, чому глобальні змінні є поганою ідеєю.
the_prole

65

Ви можете оголосити змінну остаточною або зробити її змінною примірника (або глобальної). Якщо ви визнаєте його остаточним, ви не зможете його змінити пізніше.

Будь-яка змінна, визначена у методі та доступ до якої є анонімним внутрішнім класом, повинна бути остаточною. В іншому випадку ви можете використовувати цю змінну у внутрішньому класі, не усвідомлюючи, що якщо змінна зміниться у внутрішньому класі, а потім вона буде використана пізніше в області, що додається, зміни, внесені у внутрішній клас, не зберігатимуться в області, що охоплює. В основному те, що відбувається у внутрішньому класі, залишається у внутрішньому класі.

Я написав тут більш поглиблене пояснення . Це також пояснює, чому екземпляри та глобальні змінні не потрібно оголошувати остаточними.


44

Помилка говорить усе, зміни:

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

до

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

87
Причина: якщо два способи бачать одну і ту ж локальну змінну, Java хоче, щоб ви поклялися, що ви не зміните її, - finalв Java говорять. Разом з відсутністю параметри посилання, це правило забезпечує, що місцеві жителі призначаються лише методом, якому вони належать. Таким чином, код є більш читабельним.
ignis

@ignis Я отримую помилку NullPointerException, addSiteButton.setOnClickListener(new View.OnClickListener() {чи маєте ви уявлення, чому це з'явиться?
PhDeOliveira

1
@PhDeOliveira NPE, як правило, кидається при виклику методу на змінній, яка містить null. Можливо, findViewById повертається null. Я не можу сказати більше, не будучи програмістом Android; Раджу відкрити окреме питання. Безумовно, це не має нічого спільного з внутрішніми класами, фіналом та ін .
ignis

25

Ось смішна відповідь.

Ви можете оголосити остаточний одноелементний масив і змінити елементи масиву все, що ви хочете. Я впевнений, що це порушує саму причину, чому це правило компілятора було реалізовано в першу чергу, але це зручно, коли ти перебуваєш у часовій обробці, як я був сьогодні.

Я фактично не можу претендувати на кредит для цього. Це було рекомендацією IntelliJ! Відчуває себе трохи хакітним. Але це не так погано, як глобальна змінна, тому я вважав, що тут варто згадати. Це лише одне рішення проблеми. Не обов’язково найкращий.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});

У наведеному вище випадку ви не змінюєте посилальний об'єкт, а зміняєте вміст всередині масиву. посилання має приємне пояснення.
Абілаш

Так, я знаю. Просто здається, що хак вирішити проблему. Дякуємо за Ваш коментар, роз'яснюючи це для інших
the_new_mr

4

Як сказав @Veger, ви можете зробити finalтак, щоб змінна була використана у внутрішньому класі.

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

Я назвав це pagerшвидше, ніж mPagerтому, що ви використовуєте його як локальну змінну в onCreateметоді. mПрефікс cusomarily зарезервований для змінних - членів класу (тобто змінні, які оголошені на початку класу і доступні для всіх методів класу).

Якщо вам дійсно потрібна змінна члена класу, вона не працює, щоб зробити її остаточною, оскільки ви не можете використовувати 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);
        }
    };
}

0
    public class ConfigureActivity extends Activity {

        EditText etOne;
        EditText etTwo;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_configure);

            Button btnConfigure = findViewById(R.id.btnConfigure1);   
            btnConfigure.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            configure();
                        }
                    });
    }

    public  void configure(){
            String one = etOne.getText().toString();
            String two = etTwo.getText().toString();
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.