3

After I use IntelliJ to inspect my code, hundreds of "Declaration can have final modifier" warnings are issued. To resolve these warnings, I essentially need to turn most of my variables into final, and the following is one typical example. This provokes me to think that I should make member variables as 'final' by default. In most cases, members, especially container classes (i.e. List or Set) ,won't get changed once they are assigned. They change internally by inserting or removing elements from them, but the references to them won't change.

public class Attribute {

    private final Insight insight;

    private final Attribute attribute;

    private final List<String> names;

    public Attribute(Insight insight, Attribute attribute) {
        attribute = attribute;
        insight = insight;
        names = new ArrayList<>();
    }
}

Does it make sense to make most variables as final by default in design classes? Is it the case that most member variables can be defined as 'final' in Java?

marlon
  • 6,029
  • 8
  • 42
  • 76
  • 4
    Make member variables final, unless they have to be non-final. You get several benefits, including guarantee they are assigned in the constructor or initializer (no unexpected NPEs because you didn't assign a value by mistake, unless you assign a null value of course); guaranteed visibility of value in all threads. – Andy Turner Oct 05 '17 at 23:42
  • Do you always add 'final' by default in your practice, Andy? I guess at least %95 above private members need to be defined as final. what's your percentage of final in your experience? – marlon Oct 05 '17 at 23:45
  • 2
    Yes. I'd say it's closer to 99% of the time, if not more. Mutability is overrated. – Andy Turner Oct 05 '17 at 23:45
  • OK. I feel more assured in adding the keyword to my design, although it looks silly by doing this mechanically. – marlon Oct 05 '17 at 23:47
  • The thing I am curious about is if it's going to help anyway in the performance., for example because the values are never going to be modified again maybe they are stored in a specific memory location with faster access rather than normal variables, of course we have to take into consideration about the memory architecture as well, but still does it going to make any change ( noticeable or not ) in the app performance? – JKostikiadis Oct 05 '17 at 23:49
  • 2
    @JKostikiadis https://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Jacob G. Oct 05 '17 at 23:49
  • @JacobG. thanks I make a search before posting but i guess i lose that one – JKostikiadis Oct 05 '17 at 23:51
  • @martin just get intellij to fix them all automatically; then you have the fun task of working out why the ones it *couldn't* make final can't be made final. I'd bet you'll find a few unassigned variables, and maybe some unintentional reassignments. – Andy Turner Oct 05 '17 at 23:52

4 Answers4

3

If one were to redesign Java today variables should probably default to private and final rather than package and mutable.

Mutable variables are sometimes necessary but whenever possible you should prefer final, and I can't really think of many legitimate reasons to make variables anything but private.

Things might be different for different target environments but Java is designed to make good readable/maintainable long-lived code, and considering this they just made a few poor decisions on defaults.

But it's an old language and we didn't know as much as we do now. live and learn.

ps: I'd also throw out checked exceptions if I was in charge... probably a good thing I'm not.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • 1
    If one were to redesign Java today, it'd be Kotlin ;) – Jacob G. Oct 05 '17 at 23:54
  • Perhaps, although I see Kotlin as targeting a slightly different audience there is still a LOT of overlap and if one were to start over it would probably be preferable. Personally I'm used to the C style syntax and Kotlin's style (what would you call it, action script style?) throws me off a little. – Bill K Oct 05 '17 at 23:57
  • @Bill If `final` were the default, we wouldn't have the keyword at all. We would then need an opposite keyword, like `mutable`, which would probably be an improvement. – Bohemian Oct 06 '17 at 00:16
  • *a few poor decisions*... possibly relevant: https://stackoverflow.com/a/7284322/256196 – Bohemian Oct 06 '17 at 00:19
  • That's what I meant bohemian. Also I'd probably do Not Null By Default and have a nullable keyword (instead of annotation). Nothing is perfect but I still prefer Java over just about everything else for it's primary target audience. – Bill K Oct 13 '17 at 20:38
1

If you think about most applications in a real world environment, most of the variables can be final. There are lots of benefits to declaring them final. But I would suggest you to do it as a habit rather than using IDE suggestions and features. As a habit always think of the variables whether they need to mutate or not, if not as part of defining the variable just add final. This habit makes you write finals independent of IDE suggestions. Most IDEs are pretty good at code completion, but you never know. PS: Do keep in mind inheritance of your objects when you make this a habit.

I accidentally declared a final on method once out of habit and paid a price for it :-).

Anish Mohile
  • 183
  • 1
  • 1
  • 10
  • I would say that in the real world, most of it (fields) cannot be final due to ORMs, JAXB and other mappers, Spring field/setter injection, and proxies in case of classes. Making parameters final makes the code less readable - it is better to set a different color for a reassigned parameter in the IDE. – Meo Oct 05 '17 at 23:56
  • As for "mutate”, adding or removing an element from a list or set isn't regarded as 'mutation'. Is there any explanation for this concept? – marlon Oct 05 '17 at 23:59
1

Does it make sense that I have to make most of my variables final?

Yes. But the reasons that it does are probably different than the reasons that were imagined when the language came to life in the 1990s.

In the 90s, CPUs in desktop computing were single core, single thread. The final modifier was mostly used at that time as a means to achieve similar semantics to the consts of C.

But final has more importance now that programs run on multi-threaded, multi-core CPU's. In general, it is advantageous wherever possible to restrict and/or simplify the state space of objects that may be shared in a concurrent environment. This was not always well understood when the language was in its early days. The Calendar class in early Java (which persisted until Java 8) had a very complex state space with equally complex state transitions that made using Calendar objects in concurrent environments more difficult than it had to be. In his book, Effective Java (2nd Ed.), Joshua Bloch even said that few people really understood the state space of the Calendar class.

When instance fields are final, the number of state transitions they can undergo is necessarily restricted. This is a good thing because this makes objects in a concurrent environment simpler to reason about. The ultimately simple object is an immutable object in which all instance fields are initialized in their final form and cannot be changed. Such objects are very simple to use in concurrent environments because...

  • they can never be observed in an inconsistent state,
  • never undergo state transitions,
  • never require mutual exclusion, and
  • never have stale data/visibility problems.

As a result, immutable objects can be freely shared among an arbitrarily large number of threads.

TL;DR: Final instance fields make your objects simpler and easier to reason about. The expected effects are code that is simpler to understand and cheaper to maintain. Bloch recommends that you make any instance field final whenever you can.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • for container class, why isn't adding or removing elements from the container regarded as changing the state of the object? I mean even if a List or Map is defined as 'final', you can still change its internal state. In concurrent environment, it's still unsafe, even if they are final. Could you shed some light on the real semantics of being 'final'? – marlon Oct 06 '17 at 18:39
  • As you have said, object state cannot simply be defined as the values of its instance fields, as fields may reference mutable objects. Making instance fields final simplifies, but does not trivialize, the process of reasoning about the state space of an object or the state transitions that it may undergo. Even if an instance field references a mutable object, you should make it's field `final` if you can ... and if you can make that mutable object effectively final then that's even much better as your object can still be immutable. – scottb Oct 07 '17 at 00:00
0

While it makes sense that every member field that is not going to change the reference it is initialized with, should be declared final, in your case though, you can use some other best practices. You might want to provide setters (and getters) for your member attributes unless they are not modifiable after initialization. If you provide setters, IntelliJ won't complain. Also, you can suppress those warnings by following the instructions laid out here.

The short answer is

  1. You should provide setters for your private member attributes unless they are not supposed to be modified.
  2. If they are not supposed to be modified, declare them as final.
VHS
  • 9,534
  • 3
  • 19
  • 43
  • The problem with getters and setters is that, once they are not defined but not used, even for the moment, IntelliJ won't issue anther warning, 'declaration not used'. Right? – marlon Oct 06 '17 at 00:08
  • @martin, not sure if I understood `once they are not defined but not used,`. But defining setters and getters for every attribute is now a standard practice unless an attribute is not supposed to be modifiable. – VHS Oct 06 '17 at 13:58