30

I've been reading two articles (1)(2) on javaworld.com about how all class fields should be private and getter/setter methods are just as bad. An object should act on the data it has rather than allowing access to it.

I'm currently working on a University assignment for Connect Four. In designing the program the Agents playing the Game need access to the Board's state (so they can decide what to move). They also need to pass this move to the Game so it can validate it as a legal move. And during deciding what to move pieces are grouped into Threats with a start and end Points.

Board, Threat and Point objects don't really do anything. They are just there to store related data that can be accessed in a human readable way.

At the start of design I was representing Points on the board as two element int arrays, however that got annoying when creating points or referencing components of them.

So, the class:

public class Point {
    public int x;
    public int y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

Perfect in every way I can think of. Except it breaks every rule I've learned. Have I sinned?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
AnnanFay
  • 9,573
  • 15
  • 63
  • 86
  • Too bad Java doesn't have structs. A point is a perfect example of a struct. – Esteban Araya Oct 31 '11 at 20:11
  • 5
    I assume a Point cannot be changed, and for a new point you create a new object, if it is the case, you should declare `x` and `y`, as `final`, this will make your code less 'law breaking', since the x/y values won't be at "risk" of being manipulated by the user [at least not without reflection], similar to what java arrays got with length.. – amit Oct 31 '11 at 20:14
  • 3
    Yep a horrible sin, that was also committed amongst others by the original author of the Point class in the JDK, by the google people writing the SensorEvent class in Android and dozens others. No rule is absolute and having no getters/setters for a struct-like (i.e. basically just a datastorage) isn't especially noteworthy. – Voo Oct 31 '11 at 21:18
  • 4
    "Have I sinned?" No. "Will I get marked down by my teacher?" Most likely yes. – WW. Oct 31 '11 at 21:28
  • 1
    @WW: If it's the right kind of teacher, he'd still get points if he explained the reasoning behind his choice of making those fields immutable and public. – cthulhu Nov 05 '11 at 16:14
  • 1
    I want to add that in my opinion making class fields private is one of the worst behaviour people have. If your class has something to do with security then go private. Else don't, there where so many times I could saved hours of work by extending a class if he just didn't make those harmless fields private... – clankill3r Aug 04 '15 at 09:36

9 Answers9

24

Public fields expose the representation of an object to its callers, i.e. if the representation has to change, so do the callers.

By encapsulating the representation, you can enforce how callers interact with it, and can change that representation without having to modify the callers provided the public api is unchanged. In any non-trivial program, encapsulation is necessary to achieve reasonable maintainability. However, while you need capsules, their proper granularity may be larger than a single class. For instance, it makes little sense to encapsulate an Iterator from the internal representation of the Collection it operates on.

With that out of the way, let's look at your example:

public class Point {
    public int x;
    public int y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

The internal representation of that class is exceedingly unlikely to change, so hiding the structure of the representation by making the fields private has no benefit. However, I'd prevent callers from modifying a Point once it has been constructed:

public class Point {
    public final int x;
    public final int y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

so that a class that actually wishes to encapsulate its state can return its Point without leaking its internal representation, and use a given Point in its representation without capturing it. This also fits nicely with the mathematical notion of a point, which has no identity or changing state.

In designing the program the Agents playing the Game need access to the Board's state (so they can decide what to move). They also need to pass this move to the Game so it can validate it as a legal move. And during deciding what to move pieces are grouped into Threats with a start and end Points.

Board, Threat and Point objects don't really do anything. They are just there to store related data that can be accessed in a human readable way.

Now this sounds like a wasted opportunity for encapsulation: The agents should really not be permitted to arbitrarily modify the board, but be restricted to legal moves. Why is it the responsibility of class Game to decide what a legal move is, when the state being updated resides in class Board? If the Board were to validate the moves itself, no caller, and in particular no agent, could violate the rules of the game:

public class Board {
    // private fields with state

    // public methods to query state

    public void perform(Move move) throws IllegalMoveException;
}
Community
  • 1
  • 1
meriton
  • 68,356
  • 14
  • 108
  • 175
  • Great note about the Board. It also makes much easier to make GooBoard extends Board, ChessBoard extends Board, etc – DGoiko Aug 07 '19 at 19:22
16

It's not always a sin to leave a field public, but you limit yourself severely in that you're coupling the implementation to the classes using it. Say later you want to add a listener that notifies you anytime the value in X is set. You have no way to do this without refactoring everything. If you implement setX() and hide the field itself, you will only have to change the implementation of setX() to notify you that a change was made with no change to classes utilizing this method.

James
  • 2,346
  • 1
  • 16
  • 18
  • 2
    +1 for refactoring/listeners: to clarify a bit, in enterprise development objects are often reflected on based on the method names as well as the fields (JavaBean spec). It basically is the Java version of properties --and people have varying opinions on it (all of them are right). An interesting take on this subject is project lombok, they're using annotations and creative compiling as a way to address this common point of concern. I don't recommend it for homework, but it makes for rapid development. – Daniel B. Chapman Oct 31 '11 at 20:26
  • 1
    Getters and setters are nice, but I think one can really overdo it. A point is a classical example of something that'd be implemented as a struct in other C-like languages and I dare anyone to find one real world example where using public fields for something like that has ever resulted in problems. – Voo Oct 31 '11 at 21:16
  • 3
    It is worth noting that this refactoring can be performed automatically by any decent Java IDE - provided you have access to the source code of all callers. – meriton Oct 31 '11 at 21:18
  • @Voo That's easy; lots of things *expect* there to be getters, particularly reflective tools--I've run in to this maybe a dozen times in the last decade. – Dave Newton Oct 31 '11 at 23:59
  • @DaveNewton I should've been more specific. I mean having public fields for general use - additionally having getters/setters for all the tools that rely on them (or at least have it as default such as e.g. hibernate) is fine. – Voo Nov 01 '11 at 02:00
  • @Voo Well, that's what I mean--I've consistently run into issues where public fields were the wrong thing to do (not even counting Hibernate). – Dave Newton Nov 01 '11 at 02:03
8

This is one advantage that c# has over Java. YOu can declare a class that has public fields and later on change your mind use a hidden field with getter and setter; but the callers syntax is the same

public class Point
{
     public int X;
}

becomes

public class Point
{
     int m_x;
     public int X {get {return m_x;} set {m_x = value;}
}

but the caller always does

Point p;
p.X = 12;
pm100
  • 48,078
  • 23
  • 82
  • 145
  • In this case, the Point class would better be immutable, though. – Paŭlo Ebermann Nov 01 '11 at 02:22
  • 4
    While true for your own code, this is not acceptable for libraries because the binary interface of properties and fields is different, thus this is a breaking change for .dll libs. – Davor Nov 26 '14 at 11:15
5

If you know the interface won't change, it's perfectly legal to have the variable public. The problem is as the programs get more complex you would need to change the access to the field, but modern IDEs make refactoring easy, so I would say go ahead.

Roman Goyenko
  • 6,965
  • 5
  • 48
  • 81
4

Yes and no.

No, if: you're certain of your scope, and don't need anything that expects normal Java properties (getters/setters), then it's likely not a big deal.

yes, if: some of that behavior may change, like you need to abstract a calculation affecting x or y without affecting calling code, or you're using something that expects getters and/or setters.

In general, it's easiest to follow the normal Java property pattern--it eliminates one type of risk. And it's one reason I wish Java had real properties.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
4

Java's "rules" aren't absolute. In your case, when you're using objects only to store data and not providing critical behaviors, it is perfectly okay to leave the fields public.

On the other hand, for fields that you don't need or want to expose to the user, because they might only be relevant in an internal context, then you SHOULD mark the fields private and only provide getters and/or setters if you really need/want to.

The more complicated your object, the more important it is to ensure a consistent state between operations, and therefore the more likely the programmer will be to keep the object's state well encapsulated.

Platinum Azure
  • 45,269
  • 12
  • 110
  • 134
1

I see no problem with this. If a sin, it's at least not a mortal one. Vectors are another approach, but from what I've gathered vectors in java require too much overhead.

1

Public fields break the rule of encapsulation i.e protection of the data. Yes, they do hold data but by having your instance variable PUBLIC it can be accessed by any class out there in your workspace. Instance variables can be protected as well as private.Your getter and setter methods are used just so you can modify the data that your class's instance variables hold. Usually your setter methods would have some kind of validation which is why we have to protect the instance variables of corrupt data (by marking them private or protected)

In the example above you have a constructor,which is initializing your inst. variable, but the chances are that you, as the developer of this class, has the rights and know what to data to insert in order to keep the integrity of your class. Someone else working on your class might not be aware of that and might access your variables by breaking the encapsulation and your program as a whole.

Consider x=-10; when x can only be from 0 to 100 (for example). I would recommend sticking to the encapsulation principles. Hope this helps!

Mechkov
  • 4,294
  • 1
  • 17
  • 25
0

Sometimes the rules anticipate usage that you may not have considered.

What if you make a set or a hashmap of points? For that to work the way you likely desire it to work, you should implement equals() and hashcode() overloads, but then the best practice is to make the object immutable - that way you can't change the values out from under the set/map.

nsayer
  • 16,925
  • 3
  • 33
  • 51