Immutable Type: public final fields vs. getter

JavaImmutability

Java Problem Overview


I need a small Container-Class for storing some Strings which should be immutable. As String itself is an immutable type, I thought of something like that:

public final class Immu
{
  public final String foo;
  public final String bar;
  
  public Immu(final String foo, final String bar)
  {
    this.foo = foo;
    this.bar = bar;
  }
}

Many people seem to object using public fields at all and use Getters instead. IMHO this would be just boilerplate in this case, because String itself is immutable.

Other thoughts I may be missing on this one?

Java Solutions


Solution 1 - Java

I would do what you believe is simplest and clearest. If you have a data value class which is only used by a restricted number of classes. esp a package local class. then I would avoid getter/setters and use package local or public fields.

If you have a class which you expect other modules/developers to use, following a getter/setter model may be a safer approach in the long run.

Solution 2 - Java

The problem is the uniform access principle. You may later need to modify foo so that it's obtained through a method instead of being fixed, and if you exposed the field instead of a getter, you'll need to break your API.

Solution 3 - Java

This answer is obviated:

Why not

interface Immu { String getA() ; String getB ( ) }

Immu immu ( final String a , final String b )
{
       /* validation of a and b */
       return new Immu ( )
       {
              public String getA ( ) { return a ; }

              public String getB ( ) { return b ; }
       }
}

Solution 4 - Java

I found this thread hoping for some actual arguments, but the answers I've seen here didn't help me all that much. After some more research and thinking I think the following has to be considered:

  • public final looks cleanest for immutable types.
  • Mutable types could be altered by accessors even if this is not intended - in concurrent environments this could lead to a lot of headaches.
  • There can be no no-arguments constructor. This is importent if you need factory methods (e.g. for LMAX Disruptor). In a similar way instantiating your objects via reflection becomes more complicated.
  • Getters and setters can have side effects. Using public final clearly tells the programmer that no hidden magic is occuring and the object is inherently dumb :)
  • You can't return a wrapper or a derived class instance to the accessor. Then again, this is something you should know about when the field is assigned its value. In my opinion container classes should not be concerned about what to return to whom.

If you're mid development and no guideline is stopping you and the project is isolated or you have control over all involved projects I'd suggest using public final for immutable types. If you decide you need getters later on, Eclipse offers Refactor -> Encapsulate Field... which automatically creates these and adjusts all references to the field.

Solution 5 - Java

I use the public-final-field (anti?)pattern on home projects for classes which are basically an immutable data structure with a constructor, along with absolute basics like equals(), hashCode(), toString(), etc. if required. (I'm avoiding the word "struct" because of the various different language interpretations of it.)

I wouldn't bring this approach to someone else's codebase (work, public project, etc) because it would likely be inconsistent with other code, and principles like When In Rome or Least Surprise take priority.

That said, with regard to Daniel C. Sobral's and aioobe's answers, my attitude is that if the class design becomes a problem because of unforeseen developments, it's the work of 30 seconds in an IDE to privatise the fields and add accessors, and no more than 5 or 10 minutes to fix broken references unless there are hundreds of them. Anything that fails as a result gets the unit test it should have had in the first place.:-)

[Edit: Effective Java is quite firmly against the idea, while noting that it's "less harmful" on immutable fields.]

Solution 6 - Java

Forget about encapsulation, immutability, optimization and all other big words. If you are trying to write good java code, I would recommend you just use getter simply because it is java friendly, and most importantly it saves ton of time googling why.

For example, you probably would not expect using streams when you write the code, but later you found

listOfImmus.stream().map(immu -> imm.foo).collect(Collectors.toSet()); // with field
listOfImmus.stream().map(Immu::getFoo).collect(Collectors.toSet());    // with getter

Supplier<String> s = () -> immu.foo;  // with field
Supplier<String> s = immu::foo; // with getter

// final fields are hard to mock if not impossible. 
Mockito.when(immuMock.getFoo()).thenReturn("what ever");

//one day, your code is used in a java Beans which requires setter getter..
¯\_(ツ)_/¯

This list can be long or short or may be none of them makes any sense to your use case. But you have to spend time convincing yourself (or your code reviewers) why you can or should rebel against java orthodoxy.

It is better to just write the getter/setter and spent the time for something more useful: like complaining java

Solution 7 - Java

Since Java 16, you can use records.

public record Immu(String foo, String bar) {}

All of a record's attributes are automatically final and it automatically has methods like equals(…) and toString() and the constructor.

The getters of the attributes have the same name as the attributes, in this case, they are foo() and bar().

The methods can be overridden, more information is in the documentation.

Solution 8 - Java

It is not very clear if someone is going to use your code through an API. You are also missing an opportunity to validate the input, if you are going to require some later.

Solution 9 - Java

Using public final may be fine for such small job, but it cannot be adapted as a standard practice,

Consider the situation below.

Public class Portfolio {
   public final String[] stocks;
}

Of course, being immutable, this object is initialized vis constructor, and then accessed directly. Do I have to tell you the problem in it? It’s evident!

Consider your client writing the code like below -

Portfolio portfolio = PortfolioManager.get(“Anand”);
Portfolio.stocks[0] = “FB”;
portfolio.calculate();

Is this doable? Your client libraries are able to manipulate the state of your objects, or rather able to hack within your runtime representation. This is a huge security risk, and of course tools like SONAR catch it upfront. But its manageable only if you are using getter-setters.

If you are using getters, you can very well write

   Public class Portfolio {
      private final String[] stocks;
      public String[] getStocks() {
	      return Arrays.coptOf(this.stocks);
      }
   }
        

This prevents you from potential security threat.

Looking at the above example, using public final is strongly discouraged if you are using arrays. In such case, it cannot become a standard. A person like me, will refrain from using a code practice that cannot become a uniform standard across all data types. What about you?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDanielView Question on Stackoverflow
Solution 1 - JavaPeter LawreyView Answer on Stackoverflow
Solution 2 - JavaDaniel C. SobralView Answer on Stackoverflow
Solution 3 - JavaemoryView Answer on Stackoverflow
Solution 4 - JavaManagarmView Answer on Stackoverflow
Solution 5 - JavaChrisAView Answer on Stackoverflow
Solution 6 - JavaShijing LvView Answer on Stackoverflow
Solution 7 - Javamatj1View Answer on Stackoverflow
Solution 8 - Javan0rm1eView Answer on Stackoverflow
Solution 9 - JavaAnand VaidyaView Answer on Stackoverflow