Se accede a la variable dentro de la clase interna. Necesita ser declarado definitivo

116

Entonces el título lo dice todo. Recibo un error de compilación dentro de mi onClick.

Aquí está el 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
fuente
1
Si está utilizando Eclipse, puede presionar Ctrl-1 (Cmd-1 en OS X) con el error seleccionado para ver una solución rápida que le mostrará lo que necesita cambiar. Vea más aquí: depth-first.com/articles/2008/01/11/…
Intrications

Respuestas:

130

Si no desea que sea final, siempre puede convertirlo en una variable global.

Kevin Zhao
fuente
1
@KevinZhao ¿Son definitivas las variables globales una vez que se han inicializado?
the_prole
@the_prole Creo que puedes usar final en Java, pero no estoy seguro de si puedes usarlo al crear una aplicación de Android, así que buscarlo en Google podría ser una buena idea :-)
Kevin Zhao
15
En retrospectiva, usar variables globales es una mala idea si se pueden evitar, a menos que sea un principiante, en cuyo caso complicar demasiado su programa con globales es una buena experiencia de aprendizaje. Aquí hay un buen artículo que explica por qué las variables globales son una mala idea.
the_prole
65

Puede declarar la variable final o convertirla en una variable de instancia (o global). Si lo declara definitivo, no podrá cambiarlo más tarde.

Cualquier variable definida en un método y a la que acceda una clase interna anónima debe ser final. De lo contrario, podría usar esa variable en la clase interna, sin saber que si la variable cambia en la clase interna, y luego se usa más adelante en el alcance adjunto, los cambios realizados en la clase interna no persistieron en el alcance adjunto. Básicamente, lo que sucede en la clase interior se queda en la clase interior.

Escribí una explicación más detallada aquí . También explica por qué las variables globales y de instancia no necesitan ser declaradas finales.

Brendan L
fuente
44

El error lo dice todo, cambia:

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

a

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
fuente
87
Razón: si dos métodos ven la misma variable local, Java quiere que jure que no la cambiará final, en Java habla. Junto con la ausencia de parámetros por referencia, esta regla garantiza que los locales solo se asignen en el método al que pertenecen. Por tanto, el código es más legible.
ignis
@ignis Recibo un error de NullPointerException en addSiteButton.setOnClickListener(new View.OnClickListener() {¿tiene alguna idea de por qué estaría surgiendo?
PhDeOliveira
1
@PhDeOliveira NPE normalmente se lanza cuando llamas a un método en una variable que contiene null. Probablemente, findViewById está regresando null. No puedo decir más, no siendo programador de Android; Le aconsejo que abra una pregunta separada. Ciertamente, no tiene nada que ver con las clases internas, final, et similia .
Ignis
25

Aquí tienes una respuesta divertida.

Puede declarar una matriz final de un elemento y cambiar los elementos de la matriz todo lo que aparentemente desee. Estoy seguro de que rompe la razón por la que esta regla del compilador se implementó en primer lugar, pero es útil cuando estás en un límite de tiempo como yo lo estaba hoy.

De hecho, no puedo reclamar crédito por este. ¡Fue la recomendación de IntelliJ! Se siente un poco loco. Pero no parece tan malo como una variable global, así que pensé que valía la pena mencionarlo aquí. Es solo una solución al problema. No necesariamente el mejor.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
the_new_mr
fuente
En el caso anterior, no está cambiando el objeto referenciado, sino cambiando el contenido dentro de la matriz. El enlace tiene una buena explicación.
Abilash
Sí, lo sé. Parece un truco para solucionar el problema. Gracias por tu comentario aclarándolo a los demás.
the_new_mr
4

Como dijo @Veger, puede hacer finalque la variable se pueda usar en la clase interna.

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

Lo llamé en pagerlugar de mPagerporque lo está usando como una variable local en el onCreatemétodo. El mprefijo se reserva habitualmente para las variables miembro de la clase (es decir, las variables que se declaran al principio de la clase y están disponibles para todos los métodos de la clase).

Si realmente necesita una variable miembro de clase, no funciona para que sea final porque no puede usar findViewByIdpara establecer su valor hasta onCreate. La solución es no utilizar una clase interna anónima. De esta manera, la mPagervariable no necesita ser declarada final y puede usarse en toda la clase.

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