Why is it a good practice to make a final field pointing to an immutable obj non public?

96 Views Asked by At

effective java has a statement about the access control: Even if a field is final and refers to an immutable object, by making it public you give up the flexibility to switch to a new internal data representation in which the field does not exist.

I don't understand what it means. Appreciate if someone can explain.

1

There are 1 best solutions below

0
On

Unlike newer languages like Kotlin and Scala, Java makes a very clear distinction between fields and methods. So if you've got a

public final Foo foo;

Then that's always pointing to an actual field in memory. It can never be a method, and it can never run arbitrary code. The call myInstance.foo is always direct memory access, which is contrary to OOP principles. When I ask an instance for something, it should be the instance's decision how to handle that request, not mine.

In Kotlin, properties can have getters and setters, so even if we have something that looks like a field, it might call a method to access it. This allows us to future-proof our code in advance. We can implement an actual instance variable now,

val foo: Foo = myConcreteInstance()

and then, down the road, when foo needs to go make a database query or something because we're scaling our application up, we can change the implementation

val foo: Foo
  get() = loadFooLazily()

private fun loadFooLazily(): Foo {
  // Some complicated code ...
}

Crucially, this affects nobody downstream. Everybody else downstream still writes myInstance.foo and works with the new code. It's not a breaking change.

We can't do this in Java. myInstance.foo is always a variable, not a method. So Effective Java suggests always accessing instance variables through a method in order to future-proof them. If we write

private final Foo foo;

public Foo getFoo() {
  return foo;
}

Then we can change getFoo in the future without breaking other code. It's a difference in philosophy: In Java, methods are the OOP construct while instance variables are strictly an implementation detail. In most newer languages, "instance variable" has been replaced with a high-level construct called a "property" which is intended to be used in an OOP way.