Auf die Variable wird innerhalb der inneren Klasse zugegriffen. Muss für endgültig erklärt werden

116

Der Titel sagt also alles. Ich erhalte einen Kompilierungsfehler in meinem onClick.

Hier ist der Code.

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
quelle
1
Wenn Sie Eclipse verwenden, können Sie Strg-1 (Cmd-1 unter OS X) drücken, wobei der Fehler ausgewählt ist, um eine Schnellkorrektur anzuzeigen, die Ihnen anzeigt, was geändert werden muss. Mehr Infos hier: depth-first.com/articles/2008/01/11/...
Intrications

Antworten:

130

Wenn Sie es nicht endgültig machen möchten, können Sie es immer einfach zu einer globalen Variablen machen.

Kevin Zhao
quelle
1
@ KevinZhao Sind globale Variablen nach ihrer Initialisierung endgültig?
the_prole
@the_prole Ich denke, Sie können final in Java verwenden, aber ich bin nicht sicher, ob Sie es beim Erstellen einer Android-App verwenden können. Googeln könnte also eine gute Idee sein :-)
Kevin Zhao
15
Rückblickend ist die Verwendung globaler Variablen eine schlechte Idee, wenn sie vermieden werden können, es sei denn, Sie sind Anfänger. In diesem Fall ist es eine gute Lernerfahrung, Ihr Programm mit globalen Variablen zu komplizieren. Hier ist ein guter Artikel, der erklärt, warum globale Variablen eine schlechte Idee sind.
the_prole
65

Sie können die Variable final deklarieren oder als Instanzvariable (oder globale Variable) festlegen. Wenn Sie es für endgültig erklären, können Sie es später nicht mehr ändern.

Jede Variable, die in einer Methode definiert ist und auf die eine anonyme innere Klasse zugreift, muss endgültig sein. Andernfalls könnten Sie diese Variable in der inneren Klasse verwenden, ohne zu wissen, dass die in der inneren Klasse vorgenommenen Änderungen im umschließenden Bereich nicht beibehalten wurden, wenn sich die Variable in der inneren Klasse ändert und später im umschließenden Bereich verwendet wird. Grundsätzlich bleibt das, was in der inneren Klasse passiert, in der inneren Klasse.

Ich habe hier eine ausführlichere Erklärung geschrieben . Außerdem wird erklärt, warum Instanz- und globale Variablen nicht als endgültig deklariert werden müssen.

Brendan L.
quelle
44

Der Fehler sagt alles, ändern Sie:

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

zu

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
quelle
87
Grund: Wenn zwei Methoden dieselbe lokale Variable sehen, möchte Java, dass Sie schwören, dass Sie sie nicht ändern werden. final in Java. Zusammen mit dem Fehlen von Referenzparametern stellt diese Regel sicher, dass die Einheimischen nur in der Methode zugewiesen werden, zu der sie gehören. Code ist somit besser lesbar.
Ignis
@ignis Ich erhalte einen NullPointerException-Fehler. Hast addSiteButton.setOnClickListener(new View.OnClickListener() {du eine Idee, warum das auftauchen würde?
PhDeOliveira
1
@PhDeOliveira NPE wird normalerweise ausgelöst, wenn Sie eine Methode für eine Variable aufrufen, die enthält null. Wahrscheinlich kehrt findViewById zurück null. Mehr kann ich nicht sagen, kein Android-Programmierer zu sein; Ich rate Ihnen, eine separate Frage zu stellen. Sicher hat es nichts mit inneren Klassen zu tun, final, et similia .
Ignis
25

Hier ist eine lustige Antwort.

Sie können ein endgültiges Array mit einem Element deklarieren und die Elemente des Arrays anscheinend beliebig ändern. Ich bin sicher, es bricht genau den Grund, warum diese Compiler-Regel überhaupt implementiert wurde, aber es ist praktisch, wenn Sie sich in einer Zeitbindung befinden, wie ich es heute war.

Ich kann eigentlich keinen Kredit für diesen beanspruchen. Es war die Empfehlung von IntelliJ! Fühlt sich ein bisschen hackig an. Aber es scheint nicht so schlimm wie eine globale Variable, deshalb dachte ich, dass es hier erwähnenswert ist. Es ist nur eine Lösung für das Problem. Nicht unbedingt der beste.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
the_new_mr
quelle
Im obigen Fall ändern Sie nicht das referenzierte Objekt, sondern den Inhalt innerhalb des Arrays. Link hat schöne Erklärung.
Abilash
Ja, ich weiß. Scheint nur ein Hack zu sein, um das Problem zu umgehen. Vielen Dank für Ihren Kommentar, der ihn für andere klarstellt.
the_new_mr
4

Wie @Veger sagte, können Sie es finalso gestalten, dass die Variable in der inneren Klasse verwendet werden kann.

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

Ich habe es pagereher genannt, als mPagerweil Sie es als lokale Variable in der onCreateMethode verwenden. Dasm Präfix ist normalerweise für Klassenmitgliedervariablen reserviert (dh Variablen, die am Anfang der Klasse deklariert werden und allen Klassenmethoden zur Verfügung stehen).

Wenn Sie tatsächlich eine Klassenmitgliedsvariable benötigen, funktioniert dies nicht, um sie endgültig zu machen, da Sie findViewByIdden Wert erst dann festlegen können onCreate. Die Lösung besteht darin, keine anonyme innere Klasse zu verwenden. Auf diese Weise muss die mPagerVariable nicht als endgültig deklariert werden und kann in der gesamten Klasse verwendet werden.

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
quelle
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
quelle