64

Having come across the concept of immutable objects recently, I would like to know the best practices for controlling access to the state. Even though the object oriented part of my brain makes me want to cower in fear at the sight of public members, I see no technical issues with something like this:

public class Foo {
    public final int x;
    public final int y;

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

I would feel more comfortable declaring the fields as private and providing getter methods for each but this seems overly complex when the state is explicitly read only.

What is the best practice for providing access to the state of an immutable object?

David Makogon
  • 69,407
  • 21
  • 141
  • 189
StickyCube
  • 1,721
  • 2
  • 17
  • 22

13 Answers13

62

It depends entirely on how you're going to use the object. Public fields aren't inherently evil, it's just bad to default everything to being public. For example the java.awt.Point class makes its x and y fields public, and they aren't even final. Your example seems like a fine use of public fields, but then again you might not want to expose all of the internal fields of another immutable object. There is no catch-all rule.

Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
  • 2
    I think the readability is subjective, but one thing it does help with is it decreased the slight overhead you get from calling an extra method just to get a field's value. – Kevin Workman Feb 20 '14 at 14:35
  • 15
    @KevinWorkman In most cases all traces of overhead will be erased by method inlining. – Marko Topolnik Feb 20 '14 at 14:36
  • The JVM JIT generally eliminates the method call overhead when you are just getting a field value. You won't notice a performance difference. – mikera Feb 20 '14 at 14:36
  • I agree that you probably won't notice anything, and premature optimization is the root of all evil. But the keywords here are "most" and "generally". Every context is different. Just something to be aware of. – Kevin Workman Feb 20 '14 at 14:37
  • @mikera embedded device dont have a JIT – AlexWien Feb 20 '14 at 14:38
  • 2
    The fact that `java.awt.Point` exposes its x/y may be a misleading example here. The `Point` class has been around since Java 1.0, where people could not rely on clever method inlining and JIT - but it was a rather performance-critical class considering its use in AWT. Today, I can not imagine a real reason to make fields public at all. I'd rather question whether there should be public *classes* in an API today, which subsumes the question of public fields... – Marco13 Feb 20 '14 at 14:44
  • @Marco13 - seconded. I wouldn't take the Java core classes as being good coding examples e.g. java.util.Date – Brian Agnew Feb 20 '14 at 14:46
  • 1
    @Marco13 Do you advise using factories for each and every Java object, no matter how simple? More generally, does the size of the code factor in any design decision you'd make? – Marko Topolnik Feb 20 '14 at 14:48
  • @Marco13 and Brian: fair enough, but again, this is all subjective. The OP is asking for a catch-all rule, and there isn't one. – Kevin Workman Feb 20 '14 at 14:50
  • 7
    @Marko Topolnik : As Kevin pointed out, there is no catch-all-rule. But I'd rather see interfaces+factories as the "default", and not public classes with public constructors. I occasionally thought "Oh, I should have used an interface instead of a concrete class here" - but I *never* thought "Oh, I should have used a concrete class instead of an interface here" ;-) – Marco13 Feb 20 '14 at 15:18
  • Like I said in my answer, using public-everything is bad *as a default* but using them *when it makes sense* doesn't automatically get you kicked out of the Java club. – Kevin Workman Feb 20 '14 at 15:56
  • 6
    @Marco13 On the other hand, my motto is "Each line of code must prove its worth." It seems to result in quite different defaults. – Marko Topolnik Feb 20 '14 at 17:47
  • 7
    @MarkoTopolnik, indeed, but I'd also think it would result in selection of a language other than Java. :) – Charles Duffy Feb 20 '14 at 22:41
  • @charlesDuffy **IF** presented with a choice, then yes. But I'm hardly alone in that respect :-) – Marko Topolnik Feb 21 '14 at 03:53
  • This non-Java programmer has never understood why one *wouldn't* want to expose data-for-public-consumption directly. You can always refactor in accessor methods when -- and *only* when -- it actually becomes necessary, can't you? Unless it's baked into a library interface, in which case you have bigger problems, starting with "dude, library interfaces still need to be defined in C". – zwol Feb 21 '14 at 04:38
  • 3
    @Zack: You can't refactor code that uses your code. When you publish your code (even internally in your company), it's nearly impossible to change interfaces. – Davor Feb 21 '14 at 09:48
  • @davor In that case, see the second half of my comment. – zwol Feb 21 '14 at 16:15
  • @Davor: Where it makes sense to abstract things is when they cross modules. Inside a module efficiency should prevail. A class like Point should never need accessor functions because it would be insane to redefine the implementation and leave the interface the same. If Point becomes an angle and distance from zero, x and y access functions are horribly, horribly wrong-headed. (cos(r)*d, sin(r)*d on *every access*???) – Zan Lynx Feb 22 '14 at 00:11
  • @Zack: Why would a lib like Swing need C interface? – Davor Feb 22 '14 at 08:35
  • 1
    @Zan Lynx: No, in that case you calculate the value of x, y once, when they change, and store them into private fields. After that, just return them from getters like before. – Davor Feb 22 '14 at 08:37
  • @Davor So it isn't locked inside the Java walled garden, for starters; because C's minimal feature set forces you to think harder about what a clean API should be in advance, reducing the odds of future problems; and finally because only C can guarantee you *binary* interface stability. – zwol Feb 22 '14 at 13:57
  • @Davor: Then the object isn't immutable anymore. You've got to add locks to protect the private fields. You've also added an abstraction penalty because the users of the object don't necessarily realize they aren't using the native representation. They might even optimize their code making an assumption about representation. Better to break all users and remove the abstraction. – Zan Lynx Feb 24 '14 at 22:27
  • 1
    @ZanLynx I don't see how that brakes immutability? The values are calculated once, during construction, and stored in private fields. After that, they are only returned in getters. Also, if you are using private fields to cache them, there is no overhead of any kind, they will probably even be inlined. – Davor Feb 25 '14 at 09:15
31

I have thought the same in the past but usually end up making variables private and using getters and setters so that later on I'll still have the option of making changes to the implementation while keeping the same interface.

This did remind me of something I read recently in "Clean Code" by Robert C. Martin. In chapter 6 he gives a slightly different perspective. For example, on page 95 he states

"Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions."

And on page 100:

The quasi-encapsulation of beans seems to make some OO purists feel better but usually provides no other benefit.

Based on the code sample, the Foo class would seem to be a data structure. So based on what I understood from the discussion in Clean Code (which is more than just the two quotes I gave), the purpose of the class is to expose data, not functionality, and having getters and setters probably does not do much good.

Again, in my experience, I have usually gone ahead and used the "bean" approach of private data with getters and setters. But then again, no one ever asked me to write a book about how to write better code so maybe Martin has something to say.

Paul J Abernathy
  • 993
  • 2
  • 12
  • 25
  • 4
    There's a scarcity of concepts in Java. Many use cases for a Java class have nothing to do with encapsulation and abstraction. Other languages provide such handy features as vectors, tuples, and hashes, with concise literal syntax. They are completely transparent and expose all their data---which is a *good* thing. – Marko Topolnik Feb 20 '14 at 15:03
  • 9
    A getter its a very weak form of encapsulation, your are exposing all the properties of your class and with exactly the same types. A setter if much worse. The methods (messages a class receives in OO theory) are orders to a object to accomplish some work, not request for information. IMMO there are very little justificable uses of getters, and i never see one for a setter. – AlfredoCasado Feb 20 '14 at 15:12
  • 5
    @AlfredoCasado The most amazing thing is how hard this obvious truth is to explain to a Java "best practitioner", a very common breed unfortunately. But note that generally, there *are* use cases for getters/setters, just not on pure data objects. For example, a configuration object may well involve some logic behind the *set property* action. – Marko Topolnik Feb 20 '14 at 19:50
  • 1
    @AlfredoCasado: +1, I'm driven mad by trying to explain this to hardcore OO guys. Heavy use of getters is a pretty solid hint that you're just writing verbose procedural code. – Phoshi Feb 21 '14 at 10:40
  • @AlfredoCasado: I hear that nonsense repeated all the time. Tell me, what does the `getX()` expose about my class? Do you actually know that there is a X field inside? Couldn't `getX()` be implemented as `getX{ return y*z}`? – Davor Feb 22 '14 at 08:40
  • 2
    @Davor off course you can do this, but this its not a "getter" this a method with a name that start by "get", not the same thing and probably a very bad name for a method. – AlfredoCasado Feb 22 '14 at 19:09
  • @AlfredoCasado It's a perfectly fine name, and that is exactly what a getter is, a method that returns a value of some object property (NOT field!) without side effects. Circle.getCircumference is going to be pretty much exactly like that (perhaps cached for performance reasons). – Davor Feb 23 '14 at 11:46
11

If your object is of local-enough usage that you don't care about the issues of breaking API changes for it in the future, there is no need to tack getters on top of the instance variables. But this is a general subject, not specific to immutable objects.

The advantage of using getters comes from one extra layer of indirection, which may come in handy if you are designing an object which will be widely used, and whose utility will extend into unforseeable future.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • ", which may come in handy if you are designing an object which will be widely used, and whose utility will extend into unforseeable future." can you provide one small example? – Geek Feb 20 '14 at 17:05
  • The simplest examples are objects which constitute the public API of a library. Within a project you may have internal modules which behave similarly to libraries. – Marko Topolnik Feb 20 '14 at 17:51
  • 1
    +1. My rule is "will I be okay with the IDEs 'encapsulate field' refactoring in the future? If so, public fields until needed otherwise." This covers creating a public library API, where my refactoring can't reach. – orip Feb 25 '14 at 20:05
  • @orip A very good way of putting it---and please share with us, how many times have you *actually* had to encapsulate a `final` field? Or any field at all, for that matter? – Marko Topolnik Feb 25 '14 at 20:07
6

Regardless of immutability, you're still exposing the implementation of this class. At some stage you'll want to change the implementation (or perhaps produce various derivations e.g. using the Point example, you may want a similar Point class using polar coordinates), and your client code is exposed to this.

The above pattern may well be useful, but I'd generally restrict it to very localised instances (e.g. passing tuples of information around - I tend to find that objects of seemingly unrelated info, however, either are bad encapsulations, or that the info is related, and my tuple transforms into a fully-fledged object)

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • If the object is of local use, this will never be an issue. Otherwise you should advise splitting into interface and implementation, and providing an abstract factory just to make sure the client doesn't depend on the concrete class. – Marko Topolnik Feb 20 '14 at 14:40
  • I'd rather write it correctly such that people can take it and re-use (directly or by example) rather than 'optimise' initially for my specific purposes – Brian Agnew Feb 20 '14 at 14:43
  • So you *would* advise a separate interface, and an abstract factory? – Marko Topolnik Feb 20 '14 at 14:43
  • Not specifically. I *would* advise precisely what I've written above, which is to say not exposing fields even if immutable – Brian Agnew Feb 20 '14 at 14:44
  • 1
    So you're not OK with public fields, but are OK with tightly coupling the client to a specific implementation? I don't see what line of reasoning brings you to choose one indirection over the other, when the practice clearly indicates both are very useful. – Marko Topolnik Feb 20 '14 at 14:45
  • I usually have a better idea of the potential uses, derivations and instantiations of classes I define as opposed to the underlying implementation (which, in my experience) change a *lot* more. YMMV, of course, but that's my general take on the issue. I will use interfaces and factories (not necessarily *abstract* factories) on a regular basis – Brian Agnew Feb 20 '14 at 14:49
  • For example, I have a package-private class, which I use to transport a tuple of data from one class to another. Does that use case mandate the use of getters? – Marko Topolnik Feb 20 '14 at 14:51
  • No. That doesn't seem unreasonable and I'm sure I've done the same in the past. You'll notice my answer relates to the fact that implementation is exposed and that this *can* cause you problems. It *doesn't* mandate a single approach with no room for flexibility – Brian Agnew Feb 20 '14 at 14:53
  • 1
    I guess your answer is worded such that it assumes the object is a part of public API and that its client is distinct from its provider. I see a lot of answers which take that for granted, in fact. Very different rules apply to code which defines a public API and to implementation code, and clearly there's at least 10 lines of implementation for each line of public API. – Marko Topolnik Feb 20 '14 at 14:58
5

The big thing to keep in mind is that function calls provide a universal interface. Any object can interact with other objects using function calls. All you have to do is define the right signatures, and away you go. The only catch is that you have to interact solely through these function calls, which often works well but can be clunky in some cases.

The main reason to expose state variables directly would be to be able to use primitive operators directly on these fields. When done well, this can enhance readability and convenience: for example, adding Complex numbers with +, or accessing a keyed collection with []. The benefits of this can be surprising, provided that your use of the syntax follows traditional conventions.

The catch is that operators are not a universal interface. Only a very specific set of built-in types can use them, these can only be used in the ways that the language expects, and you cannot define any new ones. And so, once you've defined your public interface using primitives, you've locked yourself into using that primitive, and only that primitive (and other things that can be easily cast to it). To use anything else, you have to dance around that primitive every time you interact with it, and that kills you from a DRY perspective: things can get very fragile very quickly.

Some languages make operators into a universal interface, but Java doesn't. This is not an indictment of Java: its designers chose deliberately not to include operator overloading, and they had good reasons to do so. Even when you're working with objects that seem to fit well with the traditional meanings of operators, making them work in a way that actually makes sense can be surprisingly nuanced, and if you don't absolutely nail it, you're going to pay for that later. It is often much easier to make a function-based interface readable and usable than to go through that process, and you often even wind up with a better result than if you'd used operators.

There were tradeoffs involved in that decision, however. There are times when an operator-based interface really does work better than a function-based one, but without operator overloading, that option just isn't available. Trying to shoehorn operators in anyway will lock you into some design decisions that you probably don't really want to be set in stone. The Java designers thought that this tradeoff was worthwhile, and they might even have been correct about that. But decisions like this don't come without some fallout, and this kind of situation is where the fallout hits.

In short, the problem isn't exposing your implementation, per se. The problem is locking yourself into that implementation.

The Spooniest
  • 2,863
  • 14
  • 14
4

Actually, it breaks encapsulation to expose any property of an object in any way -- every property is an implementation detail. Just because everybody does this doesn't make it right. Using accessors and mutators (getters and setters) doesn't make it any better. Rather, the CQRS patterns should be used to maintain encapsulation.

Software Engineer
  • 15,457
  • 7
  • 74
  • 102
3

I know only one prop to have getters for final properties. It is the case when you'd like to have access to the properties over an interface.

    public interface Point {
       int getX();
       int getY();
    }

    public class Foo implements Point {...}
    public class Foo2 implements Point {...}

Otherwise the public final fields are OK.

AnatolyG
  • 1,557
  • 8
  • 14
3

The class that you have developed, should be fine in its current incarnation. The issues usually come in play when somebody tries to change this class or inherit from it.

For example, after seeing above code, somebody thinks of adding another member variable instance of class Bar.

public class Foo {
    public final int x;
    public final int y;
    public final Bar z;

    public Foo( int x, int y, Bar z) {
        this.x = x;
        this.y = y;
    }
}

public class Bar {
    public int age; //Oops this is not final, may be a mistake but still
    public Bar(int age) {
        this.age = age;
    }
}

In above code, the instance of Bar cannot be changed but externally, anybody can update the value of Bar.age.

The best practice is to mark all fields as private, have getters for the fields. If you are returning an object or collection, make sure to return unmodifiable version.

Immunatability is essential for concurrent programming.

Shilpa
  • 106
  • 5
2

An object with public final fields that get loaded from public constructor parameters effectively portrays itself as being a simple data holder. While such data holders aren't particularly "OOP-ish", they are useful for allowing a single field, variable, parameter, or return value to encapsulate multiple values. If the purpose of a type is to serve as a simple means of gluing a few values together, such a data holder is often the best representation in a framework without real value types.

Consider the question of what you would like to have happen if some method Foo wants to give a caller a Point3d which encapsulates "X=5, Y=23, Z=57", and it happens to have a reference to a Point3d where X=5, Y=23, and Z=57. If the thing Foo has is known to be a simple immutable data holder, then Foo should simply give the caller a reference to it. If, however, it might be something else (e.g. it might contain additional information beyond X, Y, and Z), then Foo should create a new simple data holder containing "X=5, Y=23, Z=57" and give the caller a reference to that.

Having Point3d be sealed and expose its contents as public final fields will imply that methods like Foo may assume it's a simple immutable data holder and may safely share references to instances of it. If code exists that make such assumptions, it may be difficult or impossible to change Point3d to be anything other than a simple immutable data holder without breaking such code. On the other hand, code which assumes Point3d is a simple immutable data holder can be much simpler and more efficient than code which has to deal with the possibility of it being something else.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

You see this style a lot in Scala, but there is a crucial difference between these languages: Scala follows the Uniform Access Principle, but Java doesn't. That means your design is fine as long as your class doesn't change, but it can break in several ways when you need to adapt your functionality:

  • you need to extract an interface or super class (e.g. your class represents complex numbers, and you want to have a sibling class with polar coordinate representation, too)
  • you need to inherit from your class, and information becomes redundant (e.g. x can be calculated from additional data of the sub-class)
  • you need to test for constraints (e.g. x must be non-negative for some reason)

Also note that you can't use this style for mutable members (like the infamous java.util.Date). Only with getters you have a chance to make a defensive copy, or to change representation (e.g. storing the Date information as long)

Landei
  • 54,104
  • 13
  • 100
  • 195
1

I use a lot constructions very similar to the one you put in the question, sometimes there are things that can be better modeled with a (sometimes inmutable) data-strcuture than with a class.

All depends, if you are modeling an object, an object its defined by its behaviors, in this case never expose internal properties. Other times you are modeling a data-structure, and java has no special construct for data-structures, its fine to use a class and make public all the properties, and if you want immutability final and public off course.

For example, robert martin has one chapter about this in the great book Clean Code, a must read in my opinion.

AlfredoCasado
  • 818
  • 5
  • 7
1

In cases where the only purpose is to couple two values to each other under a meaningful name, you may even consider to skip defining any constructors and keep the elements changeable:

public class Sculpture {
    public int weight = 0;
    public int price = 0;
}

This has the advantage, to minimize the risk to confuse the parameter order when instantiating the class. The restricted changeability, if needed, can be achieved by taking the whole container under private control.

Wolf
  • 9,679
  • 7
  • 62
  • 108
0

Just want to reflect reflection:

Foo foo = new Foo(0, 1);  // x=0, y=1
Field fieldX = Foo.class.getField("x");
fieldX.setAccessible(true);
fieldX.set(foo, 5);
System.out.println(foo.x);   // 5!

So, is Foo still immutable? :)

bobbel
  • 3,327
  • 2
  • 26
  • 43
  • 1
    You can use reflection to call private methods too for example, and the people are still calling private methods private. Of course Foo its immutable despite the tricks you can use to violate this immutability. There are others, used by very famous libraries in java-land, like byte-code enhancement. – AlfredoCasado Feb 20 '14 at 15:09
  • That's right... However, I hate reflection because of this. With reflection you can do all things, you are usually not allowed to do. But finally you **can**! – bobbel Feb 20 '14 at 15:16
  • At least in java you have security managers and you can protect your code for "unathorized access" including reflection, not the most used feature in java, but its there for some extreme scenarios. If you don't like reflection you probably love ruby, python or javascript :P – AlfredoCasado Feb 20 '14 at 15:22