A variável é acessada dentro da classe interna. Precisa ser declarado final

116

Portanto, o título diz tudo. Estou recebendo um erro de compilação dentro do meu onClick.

Aqui está o código.

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
fonte
1
Se estiver usando o Eclipse, você pode pressionar Ctrl-1 (Cmd-1 no OS X) com o erro selecionado para ver uma Correção Rápida que mostra o que precisa ser alterado. Veja mais aqui: depth-first.com/articles/2008/01/11/...
Intrications

Respostas:

130

Se você não quiser torná-lo final, você pode apenas torná-lo uma variável global.

Kevin zhao
fonte
1
@KevinZhao As variáveis ​​globais são finais depois de inicializadas?
the_prole
@the_prole Acho que você pode usar o final em Java, mas não tenho certeza se você pode usá-lo ao criar um aplicativo Android, então pesquisar no Google pode ser uma boa ideia :-)
Kevin Zhao
15
Em retrospecto, usar variáveis ​​globais é uma má ideia se elas puderem ser evitadas, a menos que você seja um iniciante; nesse caso, complicar demais seu programa com globais é uma boa experiência de aprendizado. Aqui está um bom artigo que explica por que variáveis ​​globais são uma má ideia.
the_prole
65

Você pode declarar a variável final ou torná-la uma variável de instância (ou global). Se você declará-lo final, não será possível alterá-lo mais tarde.

Qualquer variável definida em um método e acessada por uma classe interna anônima deve ser final. Caso contrário, você poderia usar essa variável na classe interna, sem saber que, se a variável mudar na classe interna e, em seguida, for usada posteriormente no escopo interno, as mudanças feitas na classe interna não persistiram no escopo interno. Basicamente, o que acontece na classe interna permanece na classe interna.

Eu escrevi uma explicação mais detalhada aqui . Também explica por que as variáveis ​​de instância e globais não precisam ser declaradas finais.

Brendan L
fonte
44

O erro diz tudo, mude:

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

para

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
fonte
87
Motivo: se dois métodos veem a mesma variável local, Java quer que você jure que não vai mudá-la - final, em linguagem Java. Junto com a ausência de parâmetros de referência, essa regra garante que os locais sejam atribuídos apenas no método a que pertencem. O código é, portanto, mais legível.
ignis de
@ignis Estou recebendo um erro NullPointerException addSiteButton.setOnClickListener(new View.OnClickListener() {, você tem alguma ideia de por que isso ocorreria?
PhDeOliveira
1
@PhDeOliveira NPE normalmente é lançado quando você chama um método em uma variável que contém null. Provavelmente, findViewById está retornando null. Não posso dizer mais, não sendo um programador Android; Aconselho você a abrir uma pergunta separada. Certamente, não tem nada a ver com classes internas, final, et similia .
ignis
25

Aqui está uma resposta engraçada.

Você pode declarar um array final de um elemento e alterar os elementos do array o quanto quiser, aparentemente. Tenho certeza de que isso quebra o motivo pelo qual essa regra do compilador foi implementada em primeiro lugar, mas é útil quando você está em uma situação difícil como eu estava hoje.

Na verdade, não posso reivindicar crédito por este. Foi recomendação do IntelliJ! Parece um pouco hacky. Mas não parece tão ruim quanto uma variável global, então achei que valeria a pena mencionar aqui. É apenas uma solução para o problema. Não necessariamente o melhor.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
the_new_mr
fonte
No caso acima, você não está alterando o objeto referenciado, mas alterando o conteúdo dentro do array. o link tem uma boa explicação.
Abilash
Sim eu conheço. Parece um hack para contornar o problema. Obrigado pelo seu comentário esclarecendo-o para os outros.
the_new_mr
4

Como @Veger disse, você pode fazer finalcom que a variável possa ser usada na classe interna.

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

Eu o chamei em pagervez de mPagerporque você o está usando como uma variável local no onCreatemétodo. O mprefixo é reservado cusomarily para variáveis ​​de membros de classe (isto é, variáveis ​​que são declaradas no início da classe e estão disponíveis para todos os métodos de classe).

Se você realmente precisa de uma variável de membro de classe, não funciona para torná-la final porque você não pode usar findViewByIdpara definir seu valor até onCreate. A solução é não usar uma classe interna anônima. Dessa forma, a mPagervariável não precisa ser declarada final e pode ser usada em toda a 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
fonte
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
fonte