I don't know the answer to the question, but I can make some arguments
"from common sense".
In the real-Java world, the set of available types is completely open
at compile time--the compiler has no idea what classes might get
loaded at run time because classloaders can do just about anything.
Even at run time, the JVM doesn't know whether you'll be loading more
types "in the future". As a result, non-final types and methods are
always have the _potential_ to be sub-typed or overridden. In
contrast, final types and methods cannot be sub-typed or overridden.
This distinction makes it easier to JIT final things. (I think it's
possible to do some of the same optimizations in both cases, you just
have to keep enough information around to undo over-aggressive
optimizations in the case that you discover you've been too
aggressive, but that's sort of besides the point.)
GWT is a completely different world. The GWT compiler has a
"closed-world" perspective. By intentional design, classloaders are
not supported in GWT, and the set of types visible at compile time is
the complete universe of types that are supported by the application
at run time. As such, non-final methods that are not overridden are
"effectively final" and non-final classes that are not sub-typed are
"effectively final". The GWT compiler has complete freedom to treat
the "effectively final" methods and types as "actually final" because
the closed-world perspective guarantees that no new types will appear
after compilation and so the distinction between "effectively final"
and "actually final" is academic. There are lots of optimizations
that the GWT compiler makes because of the closed-world perspective.
One of the most beneficial is dead code elimination: methods that are
never called aren't even represented in the generated Javascript.
To summarize, using final might help the compiler better analyze your
code, so, in some complicated cases, you might get some benefit from
using it because the compiler might be able to prove that a
type/method/instance is final where without the final keyword the
proof might be elusive. The scenarios where this is true are becoming
rarer, though, because there's lots of active work going on to improve
the compiler's optimization abilities so it gets better and better at
analyzing your code and there are more and more cases where
"effectively final" can be determined.
Given all the above, my personal take is that final is most useful as
a declaration of intent. Mutability is difficult to reason about (or,
at least, more difficult than immutability). If you are building a
library of code to be shared with others, you need to design for
re-use. On the other hand, if you're building an application and it's
easy (both technically and organizationally) for any developer to make
a change to any piece of the application, it makes more sense to
design for maintainability. One key way to do that is to limit the
influence of system components to the tightest circle you can manage.
If, down the line, you discover the circle's too tight, you just
loosen it. The benefit from this approach is that it's easier to make
changes because it's easier to reason about the system's behaviour.
Because I take this perspective, I tend to use final quite a lot.
Ian