80

Studying some classes of Android, I realized that most of the variables of methods are declared as final.

Example code taken from the class android.widget.ListView:

/**
* @return Whether the list needs to show the top fading edge
*/
private boolean showingTopFadingEdge() {
    final int listTop = mScrollY + mListPadding.top;
    return (mFirstPosition > 0) || (getChildAt(0).getTop() > listTop);
}

/**
 * @return Whether the list needs to show the bottom fading edge
 */
private boolean showingBottomFadingEdge() {
    final int childCount = getChildCount();
    final int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();
    final int lastVisiblePosition = mFirstPosition + childCount - 1;
    final int listBottom = mScrollY + getHeight() - mListPadding.bottom;
    return (lastVisiblePosition < mItemCount - 1)
                     || (bottomOfBottomChild < listBottom);
}

What is the intention of using the final keyword in these cases?

3
  • 15
    One can flip this around and ask: why declare non-final one-time variables inside a method? Perhaps they make it final unless it absolutely does not need to be. I would say that this is not a bad practice - you are more likely to avoid a couple of bugs, save a few seconds for some readers. Also, seasoned C++ hackers might get a lukewarm, albeit not warm, semi-fuzzy feeling from using final, which is like const. Doing this has the same benefit as what I call "assert-driven development". Since what can go wrong will go wrong, why not take cheap insurance against it? State modification canb evil.
    – Job
    Oct 22, 2011 at 5:14
  • 2
  • 2
    Probably they're in place to pass static code analysis tools such as PMD.
    – Spoike
    Oct 22, 2011 at 7:36

7 Answers 7

68

I would say that this is due to force of habit. The programmer that wrote this code knew as he was writing it that the values for the final variables should never be changed after assignment, and so made them final. Any attempt to assign a new value to a final variable after assignment will result in a compiler error.

As habits go, it's not a bad one to develop. At the least, making a variable final specifies the intent of the programmer at the time of writing. This is important as it might give subsequent programmers who edit the code pause for thought before they start changing how that variable is used.

4
  • 4
    I would add (and did add in my answer) that declaring variables final by default is also a good habit because it encourages you do make fields final by default as well. Oct 22, 2011 at 11:42
  • 10
    Agreed. It's a good habit to get into along with a number of others; eg: "Make everything final by default, unless otherwise required", "Make everything private by default, unless otherwise required".
    – Jon
    Dec 3, 2011 at 10:46
  • well, ya, but there are a couple of good reasons behind the habit
    – sea-rob
    Feb 21, 2014 at 15:33
  • Calling it a "habit", IMO, gives a negative connotation. It's a good practice: declare a variable how you intend for it to be used. I realize you agree, just thought I'd throw it out there.
    – Josh M.
    Jun 21, 2018 at 19:07
54

Speaking as a Java developer who makes all variables final by default (and who appreciates the fact that Eclipse can do this automatically), I find it easier to reason about my program if variables are initialized once and never changed again.

For one thing, uninitialized variables are no longer any concern, because trying to use a final variable before it has been initialized will result in a compile error. This is particularly useful for nested conditional logic, where I want to make sure that I covered all the cases:

final int result;
if (/* something */) {
  if (/* something else */) {
    result = 1;
  }
  else if (/* some other thing */) {
    result = 2;
  }
}
else {
  result = 3;
}
System.out.println(result);

Did I cover all the cases? (Hint: No.) Sure enough, this code won't even compile.

One more thing: In general, any time you know that something is always true about a variable, you should try to get your language to enforce it. We do this every day when we specify a variable's type, of course: The language will ensure that values that are not of that type cannot be stored in that variable. Likewise, if you know that a variable should not be reassigned because it already has the value that it should keep for the entire method, then you can get the language to enforce that restriction by declaring it final.

Lastly, there's the matter of habit. Others have mentioned that this is a habit (+1 to Jon for that), but let me say something about why you would want this habit. If you are declaring fields in your class and not local variables in a method, then it's possible for multiple threads to access those fields at the same time. There are some obscure exceptions, but in general, if a field is final, then every thread that uses your class will see the same value for the variable. Conversely, if a field is not final and multiple threads are using your class, you will need to worry about explicit synchronization using synchronized blocks and/or classes from java.util.concurrent. Synchronization is possible, but programming is hard enough already. ;-) So, if you just declare everything final out of habit, then many of your fields will be final and you'll spend as little time as possible worrying about synchronization and concurrency-related bugs.

For more on this habit, check out the "Minimize Mutability" tip in Joshua Bloch's Effective Java.

Edit: @Peter Taylor has pointed out that the example above would also not compile if the final keyword is removed, which is completely correct. When I advised in favor of keeping all local variables final, it's because I wanted to make examples like the following one impossible:

int result = 0;

// OK, time to cover all the cases!
if (/* something */) {
  if (/* something else */) {
    result = 1;
  }
  else if (/* some other thing */) {
    result = 2;
  }
  // Whoops, missed an "else" here. Too bad.
}
else {
  result = 3;
}
System.out.println(result);  // Works fine!

Using a new variable instead of reusing an old one is how I can tell the compiler that trying to cover the complete universe of possibilities, and using final variables forces me to use a new variable instead of recycling an old one.

Another valid complaint about this example is that you should avoid complex nested conditional logic in the first place. That's true, of course, precisely because it's hard to make sure you covered all the cases in the way that you intended. However, sometimes complex logic can't be avoided. When my logic is complex, I want my variables to be as simple to reason about as possible, which I can achieve by making sure my variables' values never change after they are initialized.

6
  • 6
    For one thing, uninitialized variables are no longer any concern, because trying to use a final variable before it has been initialized will result in a compile error. If removing the final keyword makes your example compile, you're using a defective compiler. Or, in other words, you've dedicated half of your post to a misleading statement. (If you wrote it in terms of a field rather than a variable then it would be a case where final makes a difference, but would become irrelevant to the question - just as your third point already largely is). Oct 22, 2011 at 14:16
  • The alternative to the code snippet that I had in mind isn't just the same code snippet with the word "final" removed. I'll edit my answer to make the alternative explicit. Oct 23, 2011 at 16:19
  • Good answer. Far more comprehensive than mine. I'd like to second the recommendation for "Effective Java", specifically the section on immutable objects, which is relevant to the discussion.
    – Jon
    Dec 3, 2011 at 10:44
  • 2
    "Programmers are not to be measured by their ingenuity and their logic but by the completeness of their case analysis." -- Alan Perlis, "Epigrams in Programming", reproduced here: cs.yale.edu/quotes.html
    – minopret
    Feb 12, 2013 at 20:49
  • Personally, I would write your example something like int result = someFunctionWithComplexLogic(), and then allow the compiler to ensure that function always returns a value. This also often simplifies complex logic- your example becomes if (!/* something */) return 4; else if (/* something else */) return 1; else if (/* some other thing */) return 2;, which is much simplified from your original example. It's also trivial to see just by inspection (although the compiler will also verify it for you) that all of the cases are not handled.
    – crazy2be
    Aug 9, 2013 at 21:54
7

We can't know the answer for sure (unless we ask the original developers), but my guesses are:

  1. The programmers of these methods may have felt that adding "final" better expresses the purpose of those variables.
  2. The programmers may be using a tool that automatically add "final" where it can.
  3. It could be an optimisation trick: I don't have the knowledge / tools to verify this myself, but would be interested to know either way.
6

Another reason is to be able to use them in inner anonymous classes:

public interface MyInterface {
    void foo();
} 

void myMethod() {
    final String asdf = "tada";//if not final cannot be used in inner classes
    new MyInterface() {
        @Override
        public void foo() {
             String myNewString = asdf;//does not compile only if asdf is final
        }
    }
}
2
  • 1
    Since Java 8, this is not a valid reason anymore: any non-modified variable is considered by the compiler as effectively final, so that the final keyword don't need to be added in the proposed example. Also note that there is a missing ";" before the last "}" May 6, 2018 at 22:36
  • 1
    @JulienKronegg The comment was from 5 years ago, when Java 8 wasn't released.
    – Random42
    May 8, 2018 at 9:25
4

To make silly mistakes less frequent. final makes sure that the variable always points to its first-assigned object, and any attempted changes will count as a compile-time error.

You could also ask "why explicitly declare the type of a variable?", or "why declare methods as constant in C++?". Same reason.

2

Here the reason why: Avoiding reassignment to local variables/parameters. This would increase readability and avoid some stupid bugs.

http://sourcemaking.com/refactoring/remove-assignments-to-parameters

Excerpt:

Java 1.1 and later versions allow you to mark a parameter as final; this prevents assignment to the variable. It still allows you to modify the object the variable refers to. I always treat my parameters as final, but I confess I rarely mark them so in the parameter list.

0

Honestly, I don't think there would be a real reason to make a variable final in this context. I suppose they either copy and pasted the code, or they just really want to discourage anyone who adds more code to that method from making reassignments to those variables.

Not the answer you're looking for? Browse other questions tagged or ask your own question.