147 votes

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

J'obtiens une erreur de compilation dans mon 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);

}

157voto

Kevin Zhao Points 1965

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

83voto

Brendan L Points 843

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

Toute variable définie dans une méthode et accessible par une classe interne anonyme doit être finale. Sinon, vous pourriez utiliser cette variable dans la classe interne, sans savoir que si la variable change dans la classe interne, et qu'elle est ensuite utilisée dans la portée englobante, les changements effectués dans la classe interne n'ont pas persisté dans la portée englobante. En fait, ce qui se passe dans la classe interne reste dans la classe interne.

J'ai écrit un plus explication approfondie ici . Il explique également pourquoi les variables d'instance et globales ne doivent pas être déclarées finales.

49voto

Veger Points 17657

L'erreur dit tout, le changement :

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

à

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

36voto

the_new_mr Points 410

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 va à l'encontre de la raison même pour laquelle cette règle de compilation a été mise en place en premier lieu, mais c'est pratique lorsque vous êtes dans une situation de contrainte de temps, comme c'était mon cas aujourd'hui.

En fait, je ne peux pas m'attribuer le mérite de celle-ci. C'était la recommandation d'IntelliJ ! C'est un peu compliqué. Mais comme elle ne semble pas aussi mauvaise qu'une variable globale, j'ai pensé qu'elle méritait d'être mentionnée ici. C'est juste une solution au problème. Pas nécessairement la meilleure.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});

6voto

Suragch Points 197

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

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

Je l'ai appelé pager plutôt que mPager parce que vous l'utilisez comme une variable locale dans le fichier onCreate méthode. Le site m 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 qui sont disponibles pour toutes les méthodes de la classe).

Si vous avez réellement besoin d'une variable membre d'une classe, il est inutile de la rendre finale car vous ne pouvez pas utiliser la fonction findViewById pour fixer sa valeur jusqu'à ce que onCreate . La solution consiste à ne pas utiliser une classe interne anonyme. De cette façon, la mPager 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);
        }
    };
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X