147

I'm getting a compilation error inside of my onClick.

Here's the 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);

}
halfer
  • 19,824
  • 17
  • 99
  • 186
PhDeOliveira
  • 2,323
  • 4
  • 22
  • 25
  • 1
    If you are using Eclipse then you can press Ctrl-1 (Cmd-1 on OS X) with the error selected to see a Quick Fix which would show you what needed to be changed. See more here: http://depth-first.com/articles/2008/01/11/my-favorite-eclipse-shortcut-quick-fix/ – Intrications Jan 20 '13 at 15:11

6 Answers6

154

If you don't want to make it final, you can always just make it a global variable.

Kevin Zhao
  • 2,113
  • 2
  • 14
  • 18
  • 1
    @KevinZhao Are global variables final once they have been initialized? – the_prole Oct 11 '15 at 22:55
  • @the_prole I think you can use final in Java, but I am not sure if you can use it when building Android app, so Googling it might be a good idea :-) – Kevin Zhao Oct 12 '15 at 20:15
  • 17
    In retrospect, using global variables is a bad idea if they can be avoided, unless you're a beginner, in which case over-complicating your program with globals is good learning experience. [Here](http://c2.com/cgi/wiki?GlobalVariablesAreBad) is a good article explaining why global variables are a bad idea. – the_prole Nov 21 '15 at 22:08
81

You can declare the variable final, or make it an instance (or global) variable. If you declare it final, you won't be able to change it later.

Any variable defined in a method and accessed by an anonymous inner class must be final. Otherwise, you could use that variable in the inner class, unaware that if the variable changes in the inner class, and then it is used later in the enclosing scope, the changes made in the inner class did not persist in the enclosing scope. Basically, what happens in the inner class stays in the inner class.

I wrote a more in-depth explanation here. It also explains why instance and global variables do not need to be declared final.

Brendan L
  • 1,436
  • 14
  • 13
50

The error says it all, change:

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

to

final ViewPager mPager = (ViewPager) findViewById(R.id.fieldspager);
Veger
  • 37,240
  • 11
  • 105
  • 116
  • 94
    Reason: if two methods see the same local variable, Java wants you to swear you will not change it - `final`, in Java speak. Together with the absence of by-reference parameters, this rule ensures that locals are only assigned in the method that they belong to. Code is thus more readable. – ignis Jan 20 '13 at 15:21
  • @ignis I'm getting a NullPointerException error on `addSiteButton.setOnClickListener(new View.OnClickListener() {` do you have any idea why that would be coming up? – PhDeOliveira Jan 21 '13 at 16:52
  • 1
    @PhDeOliveira NPE is typically thrown when you call a method on a variable that contains `null`. Probably, [findViewById](http://developer.android.com/reference/android/app/Activity.html#findViewById(int)) is returning `null`. I can't say more, not being an Android programmer; I advise you to open a separate question. Certainly, it has nothing to do with inner classes, final, _et similia_. – ignis Jan 21 '13 at 21:02
  • @ignis "if two methods see the same local variable" this is not relevant in this context. OP is using an interface and Java wants to be sure that the provided object won't be modified concurrently while it's executing a "task" on it. "Code is thus more readable." this is a plain misleading sentence. Java tries to lower concurrent modification bugs and this warning (or compilation error) has nothing to do with readability. – Farid Jan 13 '21 at 10:56
35

Here's a funny answer.

You can declare a final one-element array and change the elements of the array all you want apparently. I'm sure it breaks the very reason why this compiler rule was implemented in the first place but it's handy when you're in a time-bind as I was today.

I actually can't claim credit for this one. It was IntelliJ's recommendation! Feels a bit hacky. But doesn't seem as bad as a global variable so I thought it worth mentioning here. It's just one solution to the problem. Not necessarily the best one.

final int[] tapCount = {0};

addSiteButton.setOnClickListener(new View.OnClickListener() {

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

});
halfer
  • 19,824
  • 17
  • 99
  • 186
the_new_mr
  • 3,476
  • 5
  • 42
  • 57
  • 1
    In above case you are not changing the referenced object, but changing content inside array. [link](https://stackoverflow.com/a/15656208/3422488) has nice explanation. – Abilash Jul 17 '18 at 05:54
  • 1
    Yes I know. Just seems like a hack to workaround the issue. Thanks for your comment clarifying it for others. – the_new_mr Jul 17 '18 at 13:42
  • In this case, looks to me the global variable is better... – luke cross Sep 15 '20 at 13:53
6

As @Veger said, you can make it final so that the variable can be used in the inner class.

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

I called it pager rather than mPager because you are using it as a local variable in the onCreate method. The m prefix is cusomarily reserved for class member variables (i.e. variables that are declared at the beginning of the class and are available to all class methods).

If you actually do need a class member variable, it doesn't work to make it final because you can't use findViewById to set its value until onCreate. The solution is to not use an anonymous inner class. This way the mPager variable doesn't need to be declared final and can be used throughout the class.

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
  • 484,302
  • 314
  • 1,365
  • 1,393
2
    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
  • 3,770
  • 2
  • 30
  • 25
  • Of course this is the solution if you need to update the outer class state from an inner class. Upvote for stating the obvious! – BitByteDog Jun 07 '21 at 23:07