Dostęp do zmiennej uzyskuje się w klasie wewnętrznej. Musi zostać uznany za ostateczny

116

Tytuł mówi wszystko. Otrzymuję błąd kompilacji w moim onClick.

Oto kod.

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

}
PhDeOliveira
źródło
1
Jeśli używasz Eclipse, możesz nacisnąć Ctrl-1 (Cmd-1 w OS X) z zaznaczonym błędem, aby wyświetlić Szybką poprawkę, która pokaże, co należy zmienić. Zobacz więcej tutaj: depth-first.com/articles/2008/01/11/…
Intrications

Odpowiedzi:

130

Jeśli nie chcesz, aby była ostateczna, zawsze możesz po prostu ustawić ją jako zmienną globalną.

Kevin Zhao
źródło
1
@KevinZhao Czy zmienne globalne są ostateczne po zainicjowaniu?
the_prole
@the_prole Myślę, że możesz użyć final w Javie, ale nie jestem pewien, czy możesz go użyć podczas budowania aplikacji na Androida, więc Google to może być dobry pomysł :-)
Kevin Zhao
15
Z perspektywy czasu używanie zmiennych globalnych jest złym pomysłem, jeśli można ich uniknąć, chyba że jesteś początkującym, w takim przypadku nadmierne komplikowanie programu za pomocą zmiennych globalnych jest dobrym doświadczeniem w nauce. Oto dobry artykuł wyjaśniający, dlaczego zmienne globalne to zły pomysł.
the_prole
65

Możesz zadeklarować zmienną jako ostateczną lub uczynić ją zmienną instancji (lub globalną). Jeśli uznasz to za ostateczne, nie będziesz mógł go później zmienić.

Każda zmienna zdefiniowana w metodzie i udostępniona przez anonimową klasę wewnętrzną musi być ostateczna. W przeciwnym razie możesz użyć tej zmiennej w klasie wewnętrznej, nie wiedząc, że jeśli zmienna zmieni się w klasie wewnętrznej, a następnie zostanie użyta później w otaczającym zakresie, zmiany wprowadzone w klasie wewnętrznej nie zostaną zachowane w otaczającym zakresie. Zasadniczo to, co dzieje się w klasie wewnętrznej, pozostaje w klasie wewnętrznej.

Napisałem tutaj bardziej szczegółowe wyjaśnienie . Wyjaśnia również, dlaczego zmienne instancji i globalne nie muszą być deklarowane jako ostateczne.

Brendan L.
źródło
44

Błąd mówi wszystko, zmień:

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

do

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
źródło
87
Powód: jeśli dwie metody widzą tę samą zmienną lokalną, Java chce, abyś przysiągł, że jej nie zmienisz - finalw języku Java mów. Wraz z brakiem parametrów przez odniesienie ta reguła zapewnia, że ​​lokalne są przypisywane tylko w metodzie, do której należą. Dzięki temu kod jest bardziej czytelny.
ignis
@ignis Otrzymuję błąd NullPointerException na. addSiteButton.setOnClickListener(new View.OnClickListener() {Czy masz pojęcie, dlaczego tak się dzieje?
PhDeOliveira
1
@PhDeOliveira NPE jest zwykle generowane, gdy wywołujesz metodę na zmiennej, która zawiera null. Prawdopodobnie findViewById wraca null. Nie mogę powiedzieć nic więcej, nie będąc programistą Androida; Radzę otworzyć osobne pytanie. Z pewnością nie ma to nic wspólnego z klasami wewnętrznymi, final, et similia .
ignis
25

Oto zabawna odpowiedź.

Możesz zadeklarować ostateczną jednoelementową tablicę i zmienić elementy tablicy, jak tylko chcesz. Jestem pewien, że łamie to powód, dla którego ta reguła kompilatora została zaimplementowana w pierwszej kolejności, ale jest przydatna, gdy jesteś w ograniczonym czasie, tak jak ja dzisiaj.

Właściwie nie mogę żądać uznania za ten. To była rekomendacja IntelliJ! Czuje się trochę hacky. Ale nie wygląda tak źle jak zmienna globalna, więc pomyślałem, że warto o tym tutaj wspomnieć. To tylko jedno rozwiązanie problemu. Niekoniecznie najlepsza.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
the_new_mr
źródło
W powyższym przypadku nie zmieniasz obiektu, do którego się odwołujesz, ale zmieniasz zawartość wewnątrz tablicy. link ma ładne wyjaśnienie.
Abilash,
Tak, wiem. Wygląda na to, że jest to hack, aby obejść ten problem. Dziękuję za komentarz wyjaśniający to innym.
the_new_mr
4

Jak powiedział @Veger, możesz sprawić, finalże zmienna będzie używana w klasie wewnętrznej.

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

Nazwałem to pagerraczej niż mPagerdlatego, że używasz jej jako zmiennej lokalnej w onCreatemetodzie. mPrefiks jest cusomarily zarezerwowane dla zmiennych składowych klasy (czyli zmienne, które są zadeklarowane na początku klasy i są dostępne dla wszystkich metod klasy).

Jeśli faktycznie potrzebujesz zmiennej składowej klasy, nie działa, aby była ostateczna, ponieważ nie możesz użyć findViewByIddo ustawienia jej wartości do onCreate. Rozwiązaniem jest nieużywanie anonimowej klasy wewnętrznej. W ten sposób mPagerzmienna nie musi być deklarowana jako ostateczna i może być używana w całej klasie.

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);
        }
    };
}
Suragch
źródło
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();
    }
}
Shiv Buyya
źródło