La variable est accessible dans la classe interne. Doit être déclaré définitif

116

Donc, le titre dit tout. J'obtiens une erreur de compilation dans mon fichier onClick.

Voici le 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
la source
1
Si vous utilisez Eclipse, vous pouvez appuyer sur Ctrl-1 (Cmd-1 sur OS X) avec l'erreur sélectionnée pour voir une correction rapide qui vous montrerait ce qui devait être changé. Voir plus ici: depth-first.com/articles/2008/01/11/…
Intrications

Réponses:

130

Si vous ne voulez pas le rendre définitif, vous pouvez toujours en faire une variable globale.

Kevin Zhao
la source
1
@KevinZhao Les variables globales sont-elles définitives une fois qu'elles ont été initialisées?
the_prole
@the_prole Je pense que vous pouvez utiliser final en Java, mais je ne suis pas sûr que vous puissiez l'utiliser lors de la création d'une application Android, donc googler cela pourrait être une bonne idée :-)
Kevin Zhao
15
Rétrospectivement, l'utilisation de variables globales est une mauvaise idée si elles peuvent être évitées, sauf si vous êtes un débutant, auquel cas compliquer excessivement votre programme avec des globaux est une bonne expérience d'apprentissage. Voici un bon article expliquant pourquoi les variables globales sont une mauvaise idée.
the_prole
65

Vous pouvez déclarer la variable finale ou en faire une variable d'instance (ou globale). Si vous le déclarez définitif, vous ne pourrez pas le modifier plus tard.

Toute variable définie dans une méthode et accédée par une classe interne anonyme doit être définitive. Sinon, vous pouvez utiliser cette variable dans la classe interne, sans savoir que si la variable change dans la classe interne, puis qu'elle est utilisée plus tard dans la portée englobante, les modifications apportées à la classe interne ne sont pas conservées dans la portée englobante. Fondamentalement, ce qui se passe dans la classe interne reste dans la classe interne.

J'ai écrit une explication plus approfondie ici . Il explique également pourquoi les variables d'instance et globales n'ont pas besoin d'être déclarées finales.

Brendan L
la source
44

L'erreur dit tout, changez:

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

à

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
la source
87
Raison: si deux méthodes voient la même variable locale, Java veut que vous juriez de ne pas la changer - final, en langage Java. En plus de l'absence de paramètres par référence, cette règle garantit que les sections locales ne sont affectées que dans la méthode à laquelle elles appartiennent. Le code est donc plus lisible.
ignis
@ignis J'obtiens une erreur NullPointerException sur addSiteButton.setOnClickListener(new View.OnClickListener() {avez-vous une idée de la raison pour laquelle cela se produirait?
PhDeOliveira
1
@PhDeOliveira NPE est généralement levé lorsque vous appelez une méthode sur une variable qui contient null. Probablement, findViewById est de retour null. Je ne peux pas en dire plus, n'étant pas un programmeur Android; Je vous conseille d'ouvrir une question distincte. Certes, cela n'a rien à voir avec les classes internes, finales, et similaires .
ignis
25

Voici une réponse amusante.

Vous pouvez déclarer un tableau final à un élément et changer les éléments du tableau autant que vous voulez apparemment. Je suis sûr que cela brise la raison même pour laquelle cette règle de compilation a été implémentée en premier lieu, mais c'est pratique lorsque vous êtes dans une contrainte temporelle comme je l'étais aujourd'hui.

Je ne peux en fait pas réclamer de crédit pour celui-ci. C'était la recommandation d'IntelliJ! Se sent un peu hacky. Mais cela ne semble pas aussi mauvais qu'une variable globale, alors j'ai pensé que cela valait la peine d'être mentionné ici. Ce n'est qu'une solution au problème. Pas nécessairement le meilleur.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
the_new_mr
la source
Dans le cas ci-dessus, vous ne modifiez pas l'objet référencé, mais le contenu à l'intérieur du tableau. le lien a une belle explication.
Abilash
Oui je sais. Cela semble être un hack pour contourner le problème. Merci pour votre commentaire clarifiant pour les autres.
the_new_mr
4

Comme @Veger l'a dit, vous pouvez faire en finalsorte que la variable puisse être utilisée dans la classe interne.

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

Je l'ai appelé pagerplutôt que mPagerparce que vous l'utilisez comme variable locale dans la onCreateméthode. Le mpréfixe est généralement réservé aux variables membres de la classe (c'est-à-dire les variables qui sont déclarées au début de la classe et sont disponibles pour toutes les méthodes de classe).

Si vous avez réellement besoin d'une variable de membre de classe, cela ne fonctionne pas pour la rendre finale car vous ne pouvez pas utiliser findViewByIdpour définir sa valeur tant queonCreate . La solution est de ne pas utiliser de classe interne anonyme. De cette façon, la mPagervariable n'a pas besoin d'être déclarée finale et peut être utilisée dans toute la classe.

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
la source
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
la source