If you use the field name, you don't get the encapsulation. Encapsulation applies not only between classes, but within a class.
If at some future point, you redefine the field but leave the the accessor/setter function's signatures the same, not only will external classes that use your class not break, but internal routines in your own class also won't break.
Take for example this code, loosely based on code that shows up in the Apache Wicket framework (though the framework doesn't suffer this problem; I'm just using it illustrate):
Let's say this were the original class:
class RadioChoice {
private String prefix;
RadioChoice( String prefix ) {
this.prefix = prefix ;
setPrefix( String prefix ) {
this.prefix = prefix ;
}
}
Here we can already see a problem: the same operation this.prefix = prefix
happens in tow places. It's meant to do exactly the same thing, but since it happens in two places, that "same thing" can diverge into two different things. (Compare this to database normalization, a practice designed to prevent exactly that.)
Now, Apache Wicket has a concept of recording changes to how it renders web pages, so that those changes can be undone. It does this by storing a list of "undo" object. A RadioChoice's prefix is one of those things that can be undone, and so the 'prefix' setter must record the change:
class RadioChoice {
private String prefix;
RadioChoice( String prefix ) {
this.prefix = prefix ;
setPrefix( String prefix ) {
// save the old prefix
this.getPage().addChange( new PrefixChange( this.prefix ) );
// set the new one
this.prefix = prefix ;
}
}
Now look what happens: because the constructor set the prefix directly, no change is saved by the ctor. Had we used the setter, the ctor would automatically "do the right thing" after a refactoring. Instead, we have to refactor both the setter and the ctor.
Of course, our code is still less than optimal, in reality it should be this:
setPrefix( String prefix ) {
// save the old prefix
this.getPage().addChange( new PrefixChange( this.getPrefix() ) );
// set the new one
this.prefix = prefix ;
}
}
Scott Meyers had a series of articles on this, where he advocated that (in C++, which has free functions) a class expose only primitive functions (primitive functions: functions not possible except by the class iteslf) and that all functions that could be composed out of primitive functions be made free functions. This makes for a leaner interface, more stable interface.
In that same vein, to the extent that you can treat functions in the class as dependent only on its primitive interface, the more flexibility you have; in particular, those functions are candidate to be moved out of the class. More generally, using setters and getters insulates you from changes.