Why do you always need getters and setters in Java but not in Python?

One of the first things Java programmers are teached to do when creating a class is making all attributes private and implementing getter and setter methods for those that need to be accessed by other classes. Meanwhile in Python, defining getters for all your attributes is viewed as a bad practice. Do Python developers know no better? Or can’t Java developers resist the temptation of writing some extra lines of code?

There are two questions hidden in this one.

While I have never met someone who does not religiously make all their class attributes private and define getters and setters, I have met some who are not sure why they do it. So, first, why do you always need getters and setters in Java? Some have rightfully told me that you actually don’t need getters and setters in Java, you can make all attributes public, which is of course technically correct but always leads to the question: so, why don’t you?

It is a good practice; because of encapsulation; to avoid other classes from directly manipulating the internal state of our class. These are all correct but a bit unsatisfying answers because what one would really want to know is why. Why is it a good practice? Why would you want to avoid other classes from manipulating your internal state and how are you doing so when defining a setter that all it does is assign the value received as an argument to the corresponding attribute? Other classes can still manipulate your internal state all they want, they just need to call instance.setAttribute(value) instead of instance.attribute = value.

The real reason, at least to me, why you always need to define getters and setters in Java is because you might need to add some logic or additional behaviour in the future, even if, right now, all your getter does is return the value of its corresponding attribute. If you start by directly accessing the attribute you will end up with those direct accesses spread across a lot of methods in different classes and even packages. If you eventually need to add any logic to it, you will have to make that change in a lot of different places.

As an example, consider a class Animal with a boolean attribute vaccinated. When you start writing your program there is no additional logic so you skip the getter, make the attribute public and just access it directly everywhere you need it. Everything works fine. Later, the government drafts a new law stating that an animal should only be considered vaccinated if it has received the vaccine in the last three years. If you had a getter and were using it accross all your code, the change would be simple: just add the additional logic to the getter. But, as you don’t, you will now have to create the getter isVaccinated() and change every line of your code where you were directly accessing the attribute to, now, call the getter instead.

Makes sense? So, why don’t we always need to define them in Python? In Python, as in other languages that have Properties, like C# and Kotlin, you can start without any getters or setters because if you later need to add some logic you can simply write a getter or setter for that attribute without having to change anything on the call site, i.e., when a getter is defined for some attribute, instance.attribute will no longer be simply the value of attribute but rather the value returned by the getter you have now defined.