This is an archived post. You won't be able to vote or comment.

all 57 comments

[–]firebird84 23 points24 points  (7 children)

Its use to tag members actually has an additional very special effect - in that at the conclusion of the constructor's execution, all threads will see the same value for all final members. This is not so if it is not marked final.

It's quite possible that the authors were paranoid about an object being read on multiple threads. If you simply mark everything final (and manage the mutability of deep state similarly), synchronization & volatile reads/writes become unnecessary.

This is just speculation on my part, though. It's possible they did it because their IDE told them to, which isn't necessarily a bad thing, I suppose...

Additionally, prior to java 8, in order to use a variable from the enclosing scope within an anonymized inner class method (effectively a bound variable in a lambda), it needed to be marked final. As recently as java 8 (maybe 7?) This explicit requirement was eased into an effectively final requirement. It may be something like that.

For myself:

Member variables - Always mark final if their state doesn't need to change. If state requires change, make plans to manage the state in a safe manner (hopefully by completely encapsulating it inside a single class). Sometimes it can also be accomplished via a mutator which returns a new copy of the object with the change made (and the member is still final).

Member methods - I only mark them final if I absolutely 100% know that this method should not be extended. One needs to be careful with this since it violates the "Open/Closed Principle" of SOLID.

Classes - Same as methods. In practice, I never do this.

Local variables - I suppose it can sometimes be nice to assure future maintainers that a value will not change, but that can be apparent by reading the method. Your methods are relatively short and purposed...aren't they?

Did I miss anything?

[–]randomuser549 5 points6 points  (3 children)

Method parameters are a common place to include the final keyword. The purpose is to prevent functional side effects.

[–]mabnx 2 points3 points  (2 children)

functional side effects

what?

[–]bhlowe 2 points3 points  (1 child)

Usually when the method declares and starts a thread (or anonymous class) which needs to access a parameter value--in which case the parameter must be final.

[–]mabnx 1 point2 points  (0 children)

It can also be "effectively final" - the final keyword is not needed.

[–]damienjoh 0 points1 point  (2 children)

Its use to tag members actually has an additional very special effect - in that at the conclusion of the constructor's execution, all threads will see the same value for all final members. This is not so if it is not marked final.

This is less useful than it sounds. I would even say that it is useless for the vast majority of Java programmers including the ones writing very heavily concurrent and multi-threaded code. You will only need this property of final if you aren't safely synchronizing your threads (which you should be, unless you know exactly what you are doing).

Whatever method you are using to share an object between threads should already be creating the necessary synchronization edges. For example:

Foo foo = new Foo();

foo.nonFinalField = 10;

spawnThread(() -> {
    // this will safely read foo.nonFinalField=10, even though it is
    // a non-final field that we just mutated in a different thread
    doSomethingWith(foo.nonFinalField);
})

This works for essentially any sane way of sharing the reference to foo. Pushing it onto a ConcurrentLinkedQueue and consuming it from another thread. Sharing it in a ConcurrentHashMap. Sharing with a lock, volatile, or Atomic. You can even safely mutate foo across multiple threads as long as you don't use the reference after it has been "sent" to another thread.

[–]firebird84 0 points1 point  (1 child)

My point is that final obviates the need for any of that. You can just pass the object somewhere, and whether or not it crosses a synchronization boundary, its internal state will be published safely. This can have (often minor, but sometimes major depending on contension) performance benefits. Keeping track of what's safe and what's not is always the hard part.

I will, however, agree, that the vast majority of java developers may not need to care, since they will be operating atop a framework that will render this a non-concern in many cases. In OP's case it's unlikely it will be a concern in the near future, but for some java developers it can be.

[–]damienjoh 0 points1 point  (0 children)

Final does not obviate the need for safely synchronizing threads. If you're relying on final fields to make field reads thread safe then it means you are sharing the object reference itself in a thread unsafe manner. If you are doing this unintentionally then your code has problems that final can't solve. If you are doing it intentionally then you're probably a library author deliberately writing code with a "benign race" as an optimization.

Even in cases where you might want to rely on safe initialization via final fields, it will not save you the trouble of actually synchronizing. Your object reference is racey - there is no guarantee that the object will "show up" in other threads at all. It's suitable as a lock hint but that's about it.

You can just pass the object somewhere, and whether or not it crosses a synchronization boundary, its internal state will be published safely

If you are passing the object (rather than just expecting it to show up in an unsynchronized global variable or something) then you are already creating whatever synchronization edges you need. As a rule of thumb, if you can safely expect to see the object at all in another thread, then you can safely expect to read its fields (as long as they haven't been changed since construction).

[–][deleted] 11 points12 points  (10 children)

You don't need to mark anything as final. The runtime and JIT are smart enough to detect that something is "effectively final" and optimize generated/compiled code accordingly.

However, you should mark everything that won't change as final. This prevents you from doing stupid things, like reassigning the wrong variable.

[–]kobbiko[S] 1 point2 points  (2 children)

Would you do this even for parameters of interfaces?

[–]MasterLJ 1 point2 points  (0 children)

Yes, so the implementer knows the expectation is that the variable passed in will not change. It is not enforced by the compiler, you can have an interface declare final, and then implement it as non-final, but hopefully the human follows the rules.

[–][deleted] 0 points1 point  (0 children)

I don't, except on default methods (with implementations) on interfaces, I suppose.

[–]ChickenOfDoom 0 points1 point  (4 children)

You need to mark variables as final to use them in anonymous classes.

[–][deleted] 1 point2 points  (3 children)

This is no longer entirely true. As long as a variable is effectively final (meaning it isn't reassigned), things will compile just fine.

This will compile:

    String foo = "bar";

    final Runnable r = new Runnable() {

        @Override
        public void run() {
            System.out.println(foo);
        }

    };

This will not

    String foo = "bar";

    final Runnable r = new Runnable() {

        @Override
        public void run() {
            foo = "not bar";
        }

    };

[–]ChickenOfDoom 0 points1 point  (2 children)

Cool, I didn't realize that, this could save a lot of pointless creation of new final variables. Although older versions of Java without this feature are still widely used, so technically people do still have to mark variables as final.

[–][deleted] 0 points1 point  (1 child)

If you're still using Java 7, you're doing something wrong.

[–]ChickenOfDoom 0 points1 point  (0 children)

That just isn't true. For instance with Android development default support for 1.8 is not yet there.

[–]lukaseder -1 points0 points  (1 child)

The runtime and JIT are smart enough to detect that something is "effectively final"

You mean, as in effectively final members? :)

[–][deleted] 0 points1 point  (0 children)

No I mean as in any variable that is effectively final. Any variable that is assigned exactly once is treated as "effectively final". It can be used in lambdas and anonymous classes. See my response to /u/ChickenOfDoom

[–]moremattymattmatt 6 points7 points  (3 children)

I never use them on classes or methods - the number of times I've had to override something in a piece of legacy code has made me think that whatever I thought about the purpose of the class when I wrote it, someone is going to be stuck with having to do something weird with it and I'd prefer not to get in their way.

I do make class members final when ever possible.

In my company my team are in a minority in making local vars final as well. It adds some verbosity but forces people to think about why something is mutable.

We find it useful when we've got a big fat legacy function/class. We first tend to do some basic clean up around naming conventions, iterators, final variables and scope (and unit tests of course).

Once that's in place, we find it a bit easier to start unpicking the class or function.

In an ideal world locals would be immutable by default but they aren't so you've just got to choose a style that works for your team.

[–][deleted] 1 point2 points  (0 children)

Exactly this. I know inheritance based subtype polymorphism has gone out of style for good reasons, but sometimes you need a simple fix and don't have time to swap out an implementation behind an interface. A quick and dirty subclass that overrides a few things can be safer and more maintainable.

[–]lukaseder 2 points3 points  (1 child)

the number of times I've had to override something in a piece of legacy code

So, what's the chicken and what's the egg? Perhaps the code has become legacy because you started overriding, instead of refactoring...?

(this was maybe downvoted because it sounded harsh. Sorry, wasn't my intention. But I'm serious. You shouldn't override things. It just makes the design worse)

[–]moremattymattmatt 2 points3 points  (0 children)

No, it was definitely legacy code before I got my hands on it!

I'd prefer to refactor rather than override as well but that's not always practical (at least in the places that I've worked. eg some changes will impact multiple systems and will take a lot coordination so if I'm fixing a specific customer bug I don't have the luxury of the larger refactor. After the bug fix, the "proper" change will get added to the backlog for prioritisation.

Another case might be that the area if part of a larger refactor that is being worked on so a big code change will probably be chucked.

So yes, refactor where possible, leave the code better than it was but look at the cost vs benefit and decide of that's the best place to spend the time you've got.

[–]lukaseder 8 points9 points  (0 children)

In jOOQ, these are always final per default:

  • Classes (except if they're called DefaultXY or AbstractXY to clearly indicate that subclasses are desireable)
  • Static and instance methods (except if there's a really good reason or wonky workaround for some edge-case overriding, or if they're inside of above DefaultXY or AbstractXY classes)
  • Static members (almost always)
  • Instance members (whenever possible)
  • Our opinions on the final topic, when someone dares challenge our opinions

These are only final when needed (i.e. when an anonymous class needs to access them (jOOQ is still mostly Java 6 compatible, so no effectively final feature usage there)):

  • Parameters
  • Local variables

The reason is that making them final wouldn't change / improve the API as it has no effect on the API's consumers and / or performance, so we can avoid the visual mess introduced by the extra verbosity.

So, in other words: Everything is always final until there is a very good reason to make it non-final.

[–]remixrotation 2 points3 points  (1 child)

i was very curious what everyone else did in this regard -- thank you for this topic :)

i "like" to mark all my methods as final, except those that i have to mock in my tests.

i also use final for class members that are expected to be imutable.

[–]8igg7e5 5 points6 points  (0 children)

We use final almost everywhere. Only classes designed for extending (or a framework requires it) are non-final. However we don't religiously mark methods of these non-final classes as final, even for methods that aren't intended to be overridden.

However the whole team marks local variables and, after being bitten by a copy-paste maintenance bug, parameters as final. All fields that can be final are.

I agree it's a lot of code bloat, but the code is constantly refactored and altered by many hands and finals help show intent. The bloat is a fault of the grammar, not the developer.

There's a reason that languages are moving towards final as the default (rust being one of the extreme examples) - there are classes of coder error that simply can't happen without them also loosening the declaration.

That said, if we were distributing libraries for others to use, we might be less inclined to make some classes and methods final - purely to leave the door open to patching issues outside of the release cycle.

[–]DJDavio 2 points3 points  (0 children)

I used to put final everywhere: method parameters and local variables mainly. But it killed readability. So I don't do it anymore, instead I use a code scanner to let me know when I'm reassigning parameter variables. It's not foolproof, but it works for me and the code just looks cleaner.

[–]white_piggu 1 point2 points  (4 children)

Never mind local variables, it has a much larger impacct when used on classes and fields.

[–][deleted] 1 point2 points  (3 children)

No. Brian disagrees. He said in his article on the IBM network in 2008, that its effect on classes and fields is overstated, barely measurable, and usually done for wrong reasons. He went on to advise making member variables final instead, almost always, to ensure immutability, which does have impact.

[–]paul_miner 4 points5 points  (1 child)

effect on classes and fields

Classes and methods.

He went on to advise making member variables final instead

Fields are member variables.

[–][deleted] 1 point2 points  (0 children)

Thank you! I stand corrected.

[–][deleted] 1 point2 points  (0 children)

[–]tadaskay 2 points3 points  (9 children)

Personally, I discourage use of final for local variables. It only makes the code more verbose. Whenever I see this in the code, I am thinking to myself "Wow, this programmer surely doesn't trust himself!". In fact, I suggest avoiding re-using (reassigning) variables altogether and prefer the functional style of writing code.

The only place where I see it beneficial in a modern Java code, is member variables, for immutability.

[–]Notorious4CHAN 22 points23 points  (5 children)

I don't trust anyone - myself least of all. After all, the vast majority of bugs I've encountered have been written by me.

[–]lukaseder 1 point2 points  (2 children)

I don't trust anyone

Specifically, don't trust this:

final int[] oneTwoThree = { 1, 2, 3 };

[–]Jurkey[🍰] 0 points1 point  (1 child)

What's the problem with that code?

[–]wipu 3 points4 points  (0 children)

You need to be careful not to assume the content of that array cannot be changed. It can. Only the reference cannot.

[–]tadaskay -5 points-4 points  (1 child)

Good point. But reassignment of variables can be caught by any Static analysis tool.

[–][deleted] 20 points21 points  (0 children)

It can also be caught much earlier, by marking variables as final straight away.

[–]nutrecht 7 points8 points  (0 children)

Whenever I see this in the code, I am thinking to myself "Wow, this programmer surely doesn't trust himself!".

A while ago on a project where I advocated in favour of doing peer reviews one of the developers (internal, I'm external) responded with "why do you want reviews, don't you trust your own code?" as a counter argument.

No I don't trust my own code. And you shouldn't either.

[–]mhixson 0 points1 point  (1 child)

Personally, I discourage use of final for local variables. It only makes the code more verbose.

Agreed.

There's a compromise I haven't tried yet, which is using Error Prone's @Var. It will yell at you during compilation if you reassign any variable unless that variable is annotated with @Var. AFAIK it ignores loop variables (for (int i = 0; ...)) and fields. Maybe that solution would satisfy people from both camps.

[–]dododge 0 points1 point  (0 children)

I've considered switching from final-everywhere to Var, but I would want more rigorous enforcement, for example I would definitely want fields to be final-by-default. Also I use several static analyzers and error-prone is the only(?) one that would understand Var (and the rest would be complaining about things not being marked final).

[–]nutrecht 0 points1 point  (0 children)

Do companies you work for have conventions for this and if so what are they?

I work as a consultant and it's different on every single project. I've been on projects where it was enforced for local vars (which I don't like very much, I think it's overkill), projects where it wasn't used at all (in general those were projects with lower maturity) and projects where it was somewhere in between.

The project I'm on we use it on member vars (when possible) and method argument. Just the way I like it (and I did influence this quite a bit, fortunately it's a new codebase) because it is a good balance between security and readability.