DEV Community

scottshipp
scottshipp

Posted on

Avoid getters and setters whenever possible

Don't generate getters and setters!
Noooo!!! Don't click that generate getters and setters option!!!

I like the rule: "Don't use accessors and mutators." Like any good rule, this one is meant to be broken. But when?

First, let me be clear about what I am saying: adding getters and setters to OO classes should be the last resort after considering a series of better alternatives. I believe that a careful analysis yields the conclusion that getters and setters are harmful in the clear majority of cases.

What is the harm?

First let me point out that the "harm" I am talking about might not be any harm at all. The following is, in some cases, a perfectly reasonable class:

// Car1.java

public class Car1 {
  public Engine engine;
}
Enter fullscreen mode Exit fullscreen mode

Notice, though, that feeling of tightening in your stomach, the bristle of your hair, the tensing of the muscles that you may experience looking at something like that.

Now, I want to point out that there's no meaningful functional difference between that class, a public class with a public class member, and the following class below, a public class with a private member that is exposed by getters and setters. In both classes, Car1.java and Car2.java, we get essentially the same result.

// Car2.java

public class Car2 {
  private Engine engine;

  public Engine getEngine() {
    return engine;
  }

  public void setEngine(Engine engine) {
    this.engine = engine;
  }
}
Enter fullscreen mode Exit fullscreen mode

To show this, I read and write the engine in either Car1.java or Car2.java:

// Car1 member read and write
Car1 car1 = new Car1();
logger.debug("Car1's engine is {}.", car1.engine);
car1.engine = new HemiEngine();

// Car2 member read and write
Car2 car2 = new Car2();
logger.debug("Car2's engine is {}.", car2.getEngine());
car2.setEngine(new HemiEngine();
Enter fullscreen mode Exit fullscreen mode

The point here is that anything I can do with Car2.java, I can do with Car1.java, and vice-versa.
This is important because we've been taught to get squeamish when we see Car1.java. We see that public member sitting there and we say, not safe! Engine is not protected by anything! Anyone can do anything! Aaaaaaaaagggghhhhh!

Yet for some reason we breathe a sigh of relief when we see Car2.java. Which--I'm sorry--I personally think is funny since there's literally the same (non-existent) protections around both of these things.

What could go wrong?

The following are some of the disadvantages of public getters and setters that directly expose a single private member, have the same name, and provide no other functionality.

Getters and setters are a fake insurance policy of isolated change

One supposed advantage of getters and setters is that on the off-chance that the type of a class member needs to change, the change can be limited to inside the class by making the existing getter simply translate from the internal type to the previously-exposed type.

// Car2.java, engine changed to motor

public class Car2 {
  private Motor motor;

  public Engine getEngine() {
    return convertToEngine(motor);
  }

  public void setEngine(Engine engine) {
    this.motor = convertToMotor(engine);
  }
}
Enter fullscreen mode Exit fullscreen mode

My question is how often has the working programmer ever had to do that? I don't remember ever doing this in all my years of software. Not once have I been able to take advantage of the fake insurance policy that getters and setters provide.

Also, this argument becomes an entirely moot point if the engine had never been exposed to begin with (let's say it was kept private or package-private). Just expose behavior, rather than state, and you never need to worry about flexibility in changing implementation.

This realization that the private member should not have been exposed triggers another realization that this argument is tautological. Getters and setters expose the private member, and rest the case for their existence on the fact that the private member is exposed.

Getters and setters expose implementation details

Let's say I give you only the following API to my Car object:

 _________________________________
| Car                             |
|---------------------------------|
| + getGasAmount(): Liters        |
| + setGasAmount(liters: Liters)  |
|_________________________________|
Enter fullscreen mode Exit fullscreen mode

If you assume that this is a gas-powered car that internally tracks gasoline in liters, then you are going to be right 99.999% of the time. That's really bad and this is why getters and setters expose implementation / violate encapsulation. Now this code is brittle and hard to change. What if we want a hydrogen-fuelled car? We have to throw out this whole Car class now. It would have been better just to have behavior methods like fillUp(Fuel fuel).

Things like this are the reason why famous libraries have terrible legacy classes. Have you ever noticed how most languages have a Dictionary data structure but it's called Map in Java? Dictionary actually was an interface that was introduced in JDK 1.0, but it had problems and ultimately had to be replaced by Map.

Getters and setters can actually be dangerous

Let me tell you a story about a friend. OK? A friend I said!!!

One day this friend came into work, and found that dozens of well-known web sites in countries around the world all had the header and navigation of the parent corporation's main web site (not their own), and were using British English. The operations team was frantically restarting hundreds of servers around the globe because for the first half-hour or so that these servers ran, things functioned normally. Then (bam!) all of a sudden something would happen that made the whole thing go sideways.

The culprit was a setter method deep in the guts of a shared platform that all these different sites were using. A little piece of code that ran on a schedule happened to be updated recent to this fiasco that changed the underlying value that determined site headers and languages by calling this setter.

If you only have a getter, things can be just as bad. In Java at least, returning a reference type from a getter provides that reference to the caller and now it can be manipulated by the caller in unexpected ways. Let me demonstrate.

public class Debts {
  private List<Debt> debts;

  public List<Debt> getDebts() {
    return debts;
  }
}
Enter fullscreen mode Exit fullscreen mode

OK, that seems reasonable. I need to be able to see a person's debts to give them a statement. Huh? What's that you say? You can add debts now? Shit! How did that happen!

Debts scottsDebts = DebtTracker.lookupDebts(scott);
List<Debt> debts = scottsDebts.getDebts();

// add the debt outside scotts debts, outside the debt tracker even
debts.add(new Debt(new BigDecimal(1000000))); 

// prints a new entry with one million dollars
DebtTracker.lookupDebts(scott).printReport();
Enter fullscreen mode Exit fullscreen mode

Eek!

One way to guard against this is to return a copy instead. Another way is to have an immutable member. The best way, though, is to not expose the member in any way at all and instead bring the behavior that manipulates the member inside the class. This achieves full isolation of the implementation and creates only one place to change.

When getters make sense

Wait a second! If there are so many disadvantages to accessors and mutators, why ever use them?

I'm convinced that getters and setters which just return a class member almost never make sense. But you might write something close to getter/setter functionality as long as you are actually doing something in that method.

Two examples:

  • In a setter, before updating the state in this object according to some input, we validate the input. The input validation is additional functionality.

  • The return type of a getter is an interface. We have therefore decoupled the implementation from the exposed interface.

See, what I'm really advocating for here is a different stance and philosophy towards getters and setters. Rather than say never use accessors and mutators, I want to give you the list of options that I try to exhaust before using one:

  • My "default" is to start with a private final member, set only by the constructor. No getter or setter!

  • If another class absolutely needs to see this member, I think about why. I try to see if there is a behavior that I can expose instead, and create a method for that behavior.

  • If it's absolutely necessary for some reason, then I relax to package-private (in Java) and expose the member only to other classes in the same package, but no further.

  • OK, what about the data use case? Literally I might need to have an object to pass data across some kind of interface boundary (let's say to a file system, database, web service, or something). I still don't futz around with getters and setters. I create the class with all package-private members, and I think of and use it as just a bag of properties. I try to limit these into their own packages and layers at the boundaries of the application.

  • I would consider creating both a getter and a setter for the data use case in a public API, like let's say I am writing a library meant to be used as a dependency in a lot of other applications. But I would only consider it after exhausting all of these list items.

Wisdom of the Masters

A short postscript. Obviously, there's debate about getters and setters out there in the world. It's important to know there's clearly a camp of "masters" like Robert C. ("Uncle Bob") Martin who are proponents of avoiding getters and setters. In the book Clean Code, Martin wrote about this in chapter 6:

Beans have private variables manipulated by getters and setters. The quasi-encapsulation of beans seems to make some OO purists feel better but usually provides no other benefit.

Josh Bloch has a nuanced stance in Effective Java, Item 14 that is slightly in favor of getters and setters for public classes, and slightly against for others. He ends up basically saying that what he is really concerned about is mutability, a point I touched on above:

In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable.

Further Reading

Here's some helpful thoughts from smarter people than me.

Tell, Don't Ask

Why getter and setter methods are evil

Accessors are evil

Latest comments (92)

Collapse
 
rossdrew profile image
Ross • Edited

This seems to me like a list of cases of using something badly, then complaining they are dangerous. No, using things dangerously is dangerous. No awareness of mutable state of members (returning mutable lists of restricted data), no awareness of future proofing (space for future validation without changing APIs for example)...these are not problems with the feature, but with the user.

Collapse
 
javasavvydev profile image
javasavvydev • Edited

Don't you use inheritance with java and OOP? Creating public variables instead of access methods can cause problems down the road, it also can lead to unexpected behavior. See the example below. While you probably wouldn't have a getter for a public variable, they are public for this demonstration of showing the difference. Remember, the highest cost of software is not the initial released product, but maintaining it!

public abstract class Animal {
public String name = "animal";
public String getName() { return this.name; }
}

public class Cat extends Animal {
public String name = "KIT-CAT";
}

public class PlayWithAnimal {
public static void main (String [] args) {
Animal animal1 = new Cat();
Cat cat = new Cat();
System.out.println(animal1.name);
System.out.println(animal1.getName());
System.out.println(cat.name);
System.out.println(cat.getName());

}
}

Output: animal, KIT-CAT, KIT-CAT, KIT-CAT
So now Inheritance is less useful. You can create an animal instance that contains a cat, but without a getter method you are now unable to access the cat's name...

It's also much easier to find "usages" of methods in most IDE's when using getters and setters. This can be very useful and powerful when refactoring code. (I have had to refactor code that didn't use getters and setters and it was more painful...

Some of the examples are just bad code practices, like the ability to add debts to the list, in this case it would be better to pass a copy as you said, that does not really have anything to do with getters and setters being bad, you would have the same problem with a public variable, but it COULD be protected through a getter method returning a copy.

Collapse
 
aminmansuri profile image
hidden_dude

Getters and setters often violate the principle of encapsulation which is to NOT expose internal details of your class.

They aren't really an OO technique but rather a "construction by parts" technique popularized by UI oriented techniques from VB or PowerBuilder. They are designed so that automated UI tools can get and set attributes of a UI component like color, size, etc.. It was later used for "JavaBeans" (that also started in the UI) and then was used by frameworks like Hibernate.

But to suggest this is good OO is just because many people never saw real OO from the Smalltalk days.
If you have something like a Car with a getX() and getY() position then you set the x and y position you are overriding whatever animation sequence that car should do when you "drive to" a position. A Car should really drive to positions not simply jump across the screen.

Methods should encapsulate BEHAVIOR not simple data mutation. That being said, there is room for objects such as value objects to pass around.

Another phenomena that is becoming apparent is that in backend Web programming many of the techniques used today are not really where OO shines. OO shines more in GUI and front end designs where you have inheritance and composition between components.

So I have to agree with the article.

Collapse
 
cscbnguyen43 profile image
Binh Thanh Nguyen

Thanks, nice article.

Collapse
 
hussainakbar profile image
Hussain Akbar

(Darned cat walked over the keyboard and wiped my text)

I find Java programmers to be of the same cloth as Linux users; swearing by purity instead of concentrating on substance.

Case in point is the final declaration. It's my code. If I want to change a variable, I will. If not, not. So why use final?

This (and a boatload of similar posts) are because Java does have properties (public variables) but doesn't have any actual getters and setters. They're coincidental public methods that happen to set / read values of private variables.

I mean, just look at constructors that set multiple variables. Similarly, you can have a function for example:
public void setPerson(name, age, mobileNumber){...}

And another for:
public void setPersonAddresses(homeAddress, workAddress){...}

and not have individual setters for the above 5 variables. So, are they setters? They do the same work and the result is exactly the same. You can check for invalid values, copy arrays, etc. But, noooooo. Java purists would never deign to use them.

Now, have a look at DotNet. A VB.Net class would be:

Class Example
    Private _count As Integer

    Public Property Number() As Integer
        Get
            Return _count
        End Get
        Set(ByVal value As Integer)
            _count = value
        End Set
    End Property
End Class


`

That's a setter that's bound / fixed / glued to the property. You just use example.number = x and all the checks are carried out.

You get the ease of use with the needed functionality.

Purists: Go ahead and scream at me all you want. At the end of the day, Java still won't have any getters or setters.

Collapse
 
scottshipp profile image
scottshipp

It just sounds like you have your own brand of C# purism. In Java, the tools are there if you want to use them. "Final" keyword provides one ingredient towards immutable objects. The C# equivalent is the readonly keyword. If you want to use it, great, but if you don't, also great. I don't see anything to debate or any reason to compete Java against C#. Both have getters and setters, but different syntax for achieving the same result. C# properties are a little more compact and readable IMO which is nice. But Java is not missing anything.

Collapse
 
bonobomagno profile image
Orland

This is just bad code example. In a real work case, you will check this.prefix before overriding it. Or better, Only getName with output name+prefix.
There is really no sense making a double setter with only one setter function. Particularly if the setter function name don't explain this.
If you really want to do it in this way, you will add a second argument on the setName with a default Junior value for easy overriding the default.

But again, this problems don't exist in other languages.

Collapse
 
nallack profile image
cgray

You should never write public variables for a class in object-oriented programming.

  • You are exposing internal state, thus making it easy for someone put your class into an invalid state, it is now impossible for the class to determine if and when it is being put into an invalid state, and is why poorly designed software breaks.
  • Your class may need to perform extra functionality or provide a view of an implementing instance on a call to an accessor or mutator. You don't want to waste time swapping over instead of writing it the correct way to begin with.
  • You cannot provide an interface to public member variables, thus is not ideal to use when writing a library because the API layer can't provide access to everything needed to use your class anymore.
  • Engineers have spent thousands of hours incorporating accessors and mutators into languages to encourage their use for these reasons.

(However, in "some cases" a struct can be more suitable than a class (as seen in math libraries) where member variables should instead always be public. The only limitation you have for writing a struct is that all variables are also invariant. Some languages also don't have structs (e.g. Java), in which case you need to use a class to create a struct-like object that has no responsibility for preventing invalid state e.g. java.awt.Point has public x and y members. An awt ui object however will never return its internal state, accessors will always return a clone of an internal Point instance.)

Collapse
 
kallaste profile image
Tatiana McGarry • Edited

Getters and setters are an integral part of encapsulation in OOP. Yes, things can go wrong when you abuse a setter, but unfortunately, there is no such thing as "idiot proof" code. It used to be that programmers themselves were expected to have brains.

Everywhere I look there are new "suggested practices" popping up that seem to want to guard against someone else's poor programming messing up your code in the future, when unfortunately, that simply cannot be done. The suggestion to "avoid getters and setters whenever possible" is just another example. But inexperienced developers read an article like this one and just jump on the bandwagon. How about we simply omit them when it makes sense to do so?

Just program responsibly and think about your use cases when you need something to be inaccessible and/or immutable, and stop handicapping yourself trying to guard against any remote possibility of some future developer using poor practices in the future.

Collapse
 
aminmansuri profile image
hidden_dude

They are not part of encapsulation. They are the opposite.

An encapsulated class in "pure OO" does not expose its properties. But many people confuse this because their only exposure to "OO" has been through Java and C# (and their successors) and have had little exposure to a real OO system like Smalltalk.

Collapse
 
kallaste profile image
Tatiana McGarry • Edited

Yes, they are a totally legitimate part of encapsulation in the current paradigm of Object Oriented Programming, and will be mentioned as such in current literature about it. That isn't confusion, it is the maturation of programming language concepts. A lot has changed since 40 years ago when Smalltalk was invented. We had no idea what OOP was going to be back then, or in many ways how it was going to be used.

The argument that getters and setters aren't a valid part of encapsulation because Smalltalk did it differently is a little like debating what the World Wide Web is on the basis of how things were done in ARPANET. Quite simply, just not very relevant.

Thread Thread
 
aminmansuri profile image
hidden_dude • Edited

No. Its a misunderstanding about what encapsulation is.

If you have a computation like:

bankAcct.setBalance(bankAcct.getBalance() * bankAcct.getInterestRate())

Then you aren't understanding how OO is supposed to work. The correct encapsulation would be by having behavior based method:

bankAcct.accrueInterest()

Getters and Setters don't come from OO they come from "construction by parts" a style of programming made for automated UIs like in VB. It has its place, its related to OO, but it isn't encapsulation. It's the opposite. Its exposing your attributes to the outside world.

And plenty of OO was written by great programmers like Kent Beck and others that later helped influence how we did it in other languages. But the popularization of many techniques has little to do with good OO, and simply popularization of stuff that certain developers promoted.

Collapse
 
shaaza profile image
shaaza • Edited

Exactly! Embrace (almost) complete immutability, program with expressions, isolate mutation to a corner of your system, and you can start focusing on the problem at hand instead of orthogonal issues such as this.

It's so much easier to change software when you can read code without the mental overhead of tracking state through time. I know people who have gone back to writing OO languages but still don't let go of the ideas encompassed by the functional paradigm, and it often works out well for many cases.

For a deeper (and a level-headed) understanding of the benefits of FP and what to take away from it, I recommend the paper "Out of the Tar Pit", if folks haven't already it. github.com/papers-we-love/papers-w...

Collapse
 
ethernet profile image
ethernet

Thanks for the post. Pretty clearly & I need a rafactor in my entites.

Collapse
 
theomails profile image
Theodore Ravindranath • Edited

I think within the code base of a single self contained application, you can just start with public variables. It's not difficult to refactor and add indirection later.

But if you are sharing a jar which is going to be is used by another team, then better expose via getters and setters so that there is some wiggle room to alter the implementation. Also, in such case it would have to be done with interface in the first place so public fields are out.

There is a distinction here to be made between just any Java public class and something that is module-public (something that you would expect outside teams to use)

Collapse
 
amihaiemil profile image
Mihai A.

I'm happy to see that more people are avoiding getters and setters. But this is such a long wall of text and all of it can be sumarized into one simple idea: "model objects" (get/set JavaBeans) are not real objects!

They are lifeless toys, which we have to move ourselves, instead of relying on them to move and do the job for us. Model objects should not exist in an object oriented world at all. Here is a metaphor about it:

amihaiemil.com/2018/04/17/dolls-an...

Collapse
 
smailg profile image
SmailG

I really found useful posts here... This is not one of them

Some comments may only be visible to logged-in visitors. Sign in to view all comments.