In daily development, it is common to use final to modify classes, methods, member variables, etc. When final modifies a class, it means that the class cannot be inherited, that is, the class it modifies has no subclasses. Final modifier method, indicating that the method cannot be overridden; Final decorates a variable, indicating that it cannot be modified. However, in the process of use, it may cause a series of accidents because of non-standard use; And I did.

Things like this, I am maintaining a experience N the project of the classmate, found a log analysis platform, in the process of maintaining our application chance a lot of Java. No reason to 】 【 lang. ArrayIndexOutOfBoundsException, on different list page and came up with one or two, And it’s all in RecycleView.Adapter.

The code logic is very strange, except that the code is slightly not very standard, from beginning to end does not do any action to delete the data source, everything is in order, is it push or two clicks in a row to open the same page, onNewIntent() is not handled well, the user’s hand speed is too fast to cause the data source inconsistent?

With this doubt, I rolled a demo according to the project code, a final modification at first did not pay attention to in the code, through various simulations, anyway don’t get what I want answers, ZeiXinBuSi I again carefully compared the differences of the code, a final caught my attention, should not it is the main culprit, more think more think it is possible, Simulated changes in the data, and sure enough, that’s it.

  • The code in question looks like this
 public void setData(View view) { is = ! is;// The data source is final
        final List<String> strings = new ArrayList<>();
        for (int i = 0; i < (is ? 5 : 7); i++) {
            strings.add(is ? "Raw data" : "Replacement" + i);
        }
        if (myAdapter == null) {
          	// The data source points to strings@1 when initialized for the first time.
            myAdapter = new MyAdapter(strings);
            myAdapter.setOnClickListener(new MyAdapter.OnClickListener() {
                @Override
                public void onClick(int position) {
                  	// this point points to strings@1 in the scope of the listening callback
                    Toast.makeText(MainActivity.this, strings.get(position), Toast.LENGTH_SHORT).show(); }}); mRcl.setAdapter(myAdapter); }else {
          	// The second time the method is called, the data source strings@2 is reset for the Adapter
            // The second time the method is called, the data source strings@2 is reset for the Adapter
          	// ...
          	// The important thing is that our Adapter is initialized only once from start to finish and our listener callback still points to string@1
          	// When taking string@1 to do string@n, array transgressions and inaccurate data naturally followmyAdapter.setData(strings); }}Copy the code

conclusion

  • The code style should be standardized. For example, myAdapter and strings can be declared as member variables if they are used in the page entryPrivate final List<String> data; Private Final MyAdapter mMyAdapter;Can be initialized when a variable is declared or after onCreate. Null handling of myAdapter in code is completely unnecessary when the data source changesdata.clear(),data.addAll()Method to change the data source.
  • In such callbacks, keep an eye on whether the reference we hold is the same as the reference to our actual data source, which can cause a bug that can’t be ignored but is not easy to find.