Immutable Type: public final fields vs. getter

Viewed 16704

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?

9 Answers

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

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.

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?

Related