You are missing nothing. What you do depends entirely on your situation. However, consider the following:
Very often you have to check the parameters in the setter. For example, suppose I have a class with a field that can contain a value from 0 to 10 (ticks are not needed for the exception type below, but I turn it on for clarity):
public class Example { private int value; public Example () { } public final int getValue () { return value; } public final void setValue (int value) throws IllegalArgumentException { if (value < 0 || value > 10) throw new IllegalArgumentException("Value is out of range."); } }
Here setValue () checks the "value" to make sure that it follows the rules. We have an invariant that states that "the example will not exist with the range value." Now suppose we want to create a constructor that takes a value. You can do it:
public class Example { ... public Example (int value) { this.value = value; } ... }
As you can see, there is a problem. The new Example (11) statement will succeed, and now there is an Example that violates our rules. However, if we use setter in the constructor, we can conveniently add all the design checks to the constructor:
public class Example { ... public Example (int value) throws IllegalArgumentException { setValue(value);
Thus, there are many advantages to this.
Now there are still cases where you can directly assign values. Firstly, you may not have available setters (although I would argue that creating private or batch private setters as you wish, for the reasons mentioned above, is still desirable to support / bean reflection, and to simplify the verification into a more complex code).
Another reason may be that you might have a constructor that somehow knows in advance that real values will be assigned, and therefore does not require verification and can directly assign variables. This is usually not a compelling reason to skip using setters.
However, in general, it is generally recommended that you use setters wherever possible; this usually leads to cleaner and more understandable codes that are easier to maintain as complexity increases.
Most of the examples that you see where people set variables directly are just people who are “lazy,” which is quite acceptable if the situation justifies this (maybe you are writing a program or application for quick verification and do not want to implement a bunch of setters, for example ) There is nothing wrong with that if you keep the big picture in your head and only "lazy" when appropriate.
Something I would like to add based on some other answers here: If you redefine the setter in a subclass and the data you specify sets the invariants that are supposed to be the base class, either the corresponding final setters should be done or the base class will not must make these assumptions. If overriding setters break invariants of the base class, then a big problem arises.
You will notice that the getter / setter is final in the example above. This is due to the fact that our rule is that "any example should have a value from 0 to 10". Therefore, this rule applies to subclasses. If we did not have this rule, and if any example could take any value, we would not need the final setter and it could override subclasses.
Hope this helps.