105

Encapsulation tells me to make all or almost all fields private and expose these by getters/setters. But now libraries such as Lombok appear which allow us to expose all private fields by one short annotation @Data. It will create getters, setters and setting constructors for all private fields.

Could somebody explain to me what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why do we simply not use only public fields then? I feel we have walked a long and hard way only to return to the starting point.

Yes, there are other technologies that work through getters and setters. And we cannot use them through simple public fields. But these technologies appeared only because we had those numerous properties - private fields behind public getters/setters. If we had not the properties, these technologies would develop another way and would support public fields. And everything would be simple and we wouldn't have any need for Lombok now.

What is the sense of the whole this cycle? And has encapsulation really any sense now in real life programming?

47
  • 19
    "It will create getters, setters and setting constructors for all private fields." - The way you describe this tool, it sounds like it is maintaining encapsulation. (At least in a loose, automated, somewhat anemic-model sense.) So what exactly is the problem?
    – David
    Oct 5, 2017 at 11:31
  • 81
    Encapsulation is hiding implementation internals of the object behind its public contract (usually, an interface). Getters and setters does exactly the opposite thing - they expose the object's internals, so the problem is in getters/setters, not encapsulation.
    – skapral
    Oct 5, 2017 at 12:10
  • 23
    @VinceEmigh data classes have no encapsulation. Instances of them are values in exactly the sense that primitives are
    – Caleth
    Oct 5, 2017 at 12:57
  • 11
    @VinceEmigh using JavaBeans is not OO, it is Procedural. That the literature calls them "Objects" is a mistake of history.
    – Caleth
    Oct 5, 2017 at 13:00
  • 30
    I've put a lot of thought into this over the years; I think this is a case where the intent of OOP differed from the implementation. After studying SmallTalk it's clear what OOP's encapsulation was intended to be (i.e., each class being like an independent computer, with methods as the shared protocol), though for reasons so far unknown to me, it has definitely caught on in popularity to make these getter/setter objects that offer no conceptual encapsulation (they hide nothing, manage nothing, and have no responsibility other than data), and yet they still use properties.
    – jrh
    Oct 5, 2017 at 13:25

17 Answers 17

90

If you expose all your attributes with getters/setters you are gaining just data structure which is still used in C or any other procedural language. It is not encapsulation and Lombok is just making to work with procedural code less painful. Getters/setters as bad as plain public fields. There is no difference really.

And data structure is not an object. If you will start creating an object from writing an interface you will never add getters/setters to the interface. Exposing your attributes leads to spaghetti procedural code where manipulation with data is outside of an object and spread allover the codebase. Now you are dealing with data and with manipulations with data instead of talking to objects. With getters/setters, you will have data-driven procedural programming where manipulation with that done in the straight imperative way. Get data - do something - set data.

In OOP encapsulation is an elephant if done in right way. You should encapsulate state and implementation details so that object has full control on that. Logic will be focused inside object and will not be spread all over the codebase. And yes - encapsulation is still essential in programming as the code will be more maintainable.

EDITS

After seeing the discussions going on I want to add several things:

  • It doesn't matter how many of your attributes you expose through getters/setters and how carefully you're doing that. Being more selective will not make your code OOP with encapsulation. Every attribute you expose will lead to some procedure working with that naked data in imperative procedural way. You will just spread your code less slowly with being more selective. That doesn't change the core.
  • Yes, in boundaries within the system you get naked data from other systems or database. But this data is just another encapsulation point.
  • Objects should be reliable. The whole idea of objects is being responsible so that you don't need to give orders that are straight and imperative. Instead you're asking an object to do what it does well through the contract. You safely delegate acting part to the object. Objects encapsulate state and implementation details.

So if we return to the question why we should do this. Consider this simple example:

public class Document {
    private String title;

    public String getTitle() {
        return title;
    }
}

public class SomeDocumentServiceOrHandler {

    public void printDocument(Document document) {
        System.out.println("Title is " + document.getTitle());
    }
}

Here we have Document exposing internal detail by getter and have external procedural code in printDocument function which works with that outside of the object. Why is this bad? Because now you just have C style code. Yes it is structured but what's really the difference? You can structure C functions in different files and with names. And those so called layers are doing exactly that. Service class is just a bunch of procedures that work with data. That code is less maintainable and has many drawbacks.

public interface Printable {
    void print();
}

public final class PrintableDocument implements Printable {
    private final String title;

    public PrintableDocument(String title) {
        this.title = title;
    }

    @Override
    public void print() {
        System.out.println("Title is " + title);
    }
}

Compare with this one. Now we have a contract and the implemantation details of this contract is hidden inside of the object. Now you can truly test that class and that class is encapsulating some data. How it works with that data is an object concerns. In order to talk with object now you need to ask him to print itself. That's encapsulation and that is an object. You will gain the full power of dependency injection, mocking, testing, single responsibilities and a lot bunch of benefits with OOP.

8
  • 1
    "Getters/setters as bad as plain public fields" -- that's not true, at least in many languages. You typically cannot override plain field access, but can override getters/setters. That gives versatility to child classes. It also makes it easy to change the behavior of classes. eg, you might start with a getter/setter backed by a private field and later move to a different field or calculating the value from other values. Neither can be done with plain fields. Some languages do allow fields to have what's essentially automatic getters and setters, but Java is not such a language.
    – Kat
    Oct 11, 2017 at 0:39
  • 1
    Eh, still not convinced. Your example is okay but just denote a different style of coding, not the "true right one" - which doesn't exist. Keep in mind most languages are multiparadigm nowadays and rarely are pure OO ou pure procedural. Sticking yourself so hard to "pure OO concepts". As an example - you can't use DI on your example because you coupled the printing logic to a subclass. Printing shouldn't be part of a document - a printableDocument would expose its printable part (via a getter) to a PrinterService.
    – T. Sar
    Oct 13, 2017 at 12:36
  • 1
    A proper OO approach to this, if we were going to a "Pure OO" approach, would use something that implements an abstract PrinterService class requesting, via a message, what the hell should be printed - using a GetPrintablePart of sorts. The thing that implements the PrinterService could be any sort of printer - print to a PDF, to the screen, to the TXT... Your solution makes it impossible to swap out the printing logic for something else, ending up more coupled and less maintanable than your "wrong" example.
    – T. Sar
    Oct 13, 2017 at 12:41
  • 1
    The bottom line: Getters and Setters aren't evil or breaking OOP - People that don't understand how to use them are. You example case is a textbook example of people missing the whole point how DI works. The example you "corrected" was already DI-enabled, decoupled, could be mocked easily... Really - remember the whole "Prefer Composition over Inheritance" thing? One of the OO pillars? You just threw it by the window by the way you refactored the code. This wouldn't fly in any serious code review.
    – T. Sar
    Oct 13, 2017 at 12:42
  • 1
    Furthermore, you're not only breaking Single Responsibility the moment you add new responsibility to an object (printing itself), but you also removed the ability for it to be mocked out by subclassing it, so you won't be able to test it as easily, you removed the flexibility of someone to chose what printer to use, thus making the code more coupled and less extensible... really - your example would be way, way better if it was backwards. The one using the getter is way better than the one using inheritance.
    – T. Sar
    Oct 13, 2017 at 12:50
75

Could somebody explain me, what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why we do not simply use only public fields then?

The sense is that you are not supposed to do that.

Encapsulation means you expose only those fields you actually need other classes to access, and that you're very selective and careful about it.

Do not just give all fields getters and setters per default!

That is completely against the spirit of the JavaBeans specification which is ironically where the concept of public getters and setters comes from.

But if you look at the spec, you'll see that it intended the creation of getters and setters to be very selective, and that it talks about "read-only" properties (no setter) and "write-only" properties (no getter).

Another factor is that getters and setters are not necessarily simple access to private field. A getter can compute the value it returns in an arbitrarily complex way, maybe cache it. A setter can validate the value or notify listeners.

So there you are: encapsulation means you expose only that functionality which you actually need to expose. But if you don't think about what you need to expose and just willy-nilly expose everything by following some syntactical transformation then of course that's not really encapsulation.

12
  • 20
    "[G]etters and setters are not necessarily simple access" -- I think that's a key point. If your code is accessing a field by name, you can't change the behaviour later. If you're using obj.get(), you can. Oct 5, 2017 at 14:07
  • 4
    @jrh: I've never heard it called a bad practice in Java, and it's pretty common. Oct 5, 2017 at 14:52
  • 2
    @MichaelBorgwardt Interesting; a bit off topic but I've always wondered why Microsoft recommended against doing that for C#. I guess those guidelines imply that in C# the only thing setters can be used for is converting a value given to some other value used internally, in a way that cannot fail or have an invalid value (e.g., having a PositionInches property and a PositionCentimeters property where it automatically converts internally to mm)? Not a great example but it's the best I can come up with at the moment.
    – jrh
    Oct 5, 2017 at 15:05
  • 2
    @jrh that source says to not do it for getters, as opposed to setters. Oct 5, 2017 at 19:44
  • 3
    @Gangnus and you really see that someone is hiding some logic inside getter/setter? We are so used to it that getFieldName() become an automatic contract for us. We will not expect some complex behavior behind that. In most cases it's just straight breaking of encapsulation. Oct 6, 2017 at 16:10
18

I think the crux of the matter is explained by your comment:

I totally agree with your thought. But somewhere we have to load objects with data. For example, from XML. And current platforms supporting it do that through getters/setters, thus degrading the quality of our code and our way of thinking. Lombok, really, is not bad by itself, but its very existence shows that we have something bad.

The problem you have is that you are mixing the persistence datamodel with the active datamodel.

An application will generally have multiple datamodels:

  • a datamodel to talk to the database,
  • a datamodel to read the configuration file,
  • a datamodel to talk to another application,
  • ...

on top of the datamodel it actually uses to perform its computations.

In general, the datamodels used for communication with the outside should be isolated and independent from the inner datamodel (business object model, BOM) on which computations are performed:

  • independent: so that you can add/remove properties on the BOM as suited by your requirements without having to change all clients/servers, ...
  • isolated: so that all computations are performed on the BOM, where invariants live, and that changing from one service to another, or upgrading one service, does not cause a ripple throughout the codebase.

In this scenario, it is perfectly fine for the objects used in the communication layers to have all items public or exposed by getters/setters. Those are plain objects with no invariant whatsoever.

On the other hand, your BOM should have invariants, which generally precludes having lots of setters (getters do not affect invariants, although they do reduce encapsulation to a degree).

8
  • 3
    I wouldn't create getters and setters for communication objects. I'd just make the fields public. Why create more work than is useful?
    – user253751
    Oct 5, 2017 at 22:45
  • 3
    @immibis: I agree in general, however as noted by the OP some frameworks require getters/setters, in which case you just have to comply. Note that the OP is using a library which creates them automatically with the application of a single attribute, so it's little work for him. Oct 6, 2017 at 6:27
  • 1
    @Gangnus: The idea here is that the getters/setters are isolated to the boundary (I/O) layer, where it's normal to get dirty, but the rest of the application (pure code) is not contaminated and remains elegant. Oct 6, 2017 at 12:32
  • 2
    @kubanczyk: the official definition is Business Object Model as far as I know. It corresponds here to what I called the "inner datamodel", the datamodel on which you execute logic in the "pure" core of your application. Oct 6, 2017 at 17:38
  • 1
    This is the pragmatic approach. We live in a hybrid world. If the external libraries could have know what data to pass to the BOM constructors, then we would only had BOMs. Oct 10, 2017 at 8:28
12

Consider the following..

You have a User class with a property int age:

class User {
    int age;
}

You want to scale this up so a User has a date of birth, opposed to only an age. Using a getter:

class User {
    private int age;

    public int getAge() {
        return age;
    }
}

We can swap out the int age field for a more complex LocalDate dateOfBirth:

class User {
    private LocalDate dateOfBirth;

    public int getAge() {
        LocalDate now = LocalDate.now();
        int year = ...; // calculate using dateOfBirth and now
        return year;
    }

    // other behaviors can now make use of dateOfBirth
}

No contract violations, no code breakage.. Nothing more than scaling up the internal representation in preparation for more complex behaviors.

The field itself is encapsulated.


Now, to clear the concerns..

Lombok's @Data annotation is similar to Kotlin's data classes.

Not all classes represent behavioral objects. As for breaking encapsulation, it depends on your usage of the feature. You shouldn't be exposing all your fields via getters.

Encapsulation is used to hide the values or state of a structured data object inside a class

In a more general sense, encapsulation is the act of hiding information. If you abuse @Data, then it's easy to assume you're probably breaking encapsulation. But that's not to say it has no purpose. JavaBeans, for example, are frowned upon by some. Yet it's used extensively in enterprise development.

Would you conclude that enterprise development is bad, due to the use of beans? Of course not! The requirements differ from that of standard development. Can beans be abused? Of course! They're abused all the time!

Lombok also supports @Getter and @Setter independently - use what your requirements call for.

17
  • 3
    This is unrelated to slapping the @Data annotation on a type, which by design unencapsulates all the fields
    – Caleth
    Oct 5, 2017 at 14:56
  • 1
    No, the fields are not hidden. Because anything can come along and setAge(xyz)
    – Caleth
    Oct 5, 2017 at 15:11
  • 9
    @Caleth The fields are hidden. You're acting as if setters can't have pre and post conditions, which is a very common use case for using setters. You're acting as if a field == property, which even in languages like C#, just isn't true, as both tend to be supported (as in C#). The field can be moved to another class, it can be swapped for a more complex representation...
    – Dioxin
    Oct 5, 2017 at 15:16
  • 1
    And what I'm saying is that isn't important
    – Caleth
    Oct 5, 2017 at 15:40
  • 2
    @Caleth How is it not important? That doesn't make any sense. You're saying encapsulation doesn't apply because you feel it's not important in this situation, even though it applies by definition and has it's use cases?
    – Dioxin
    Oct 5, 2017 at 15:42
12

Encapsulation tells me to make all or almost all fields private and expose these by getters/setters.

That is not how encapsulation is defined in object oriented programming. Encapsulation means that each object should be like a capsule, whose outer shell (the public api) protects and regulates access to its interior (the private methods and fields), and hides it from view. By hiding internals, callers to not depend on internals, allowing internals to be changed without changing (or even recompiling) callers. Also, encapsulation allows each object to enforce its own invariants, by only making safe operations available to callers.

Encapsulation is therefore a special case of information hiding, in which each object hides its internals and enforces its invariants.

Generating getters and setters for all fields is a pretty weak form of encapsulation, because the structure of the internal data is not hidden, and invariants can not be enforced. It does have the advantage that you could change the way the data is stored internally (as long as you can convert to and from the old structure) without having to change (or even recompile) callers, however.

Could somebody explain to me what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why do we simply not use only public fields then? I feel we have walked a long and hard way only to return to the starting point.

Partially, this is due to a historic accident. Strike one is that, in Java, method invocation expressions and field access expressions are syntactically different at the call site, i.e. replacing a field access by a getter or setter call breaks the API of a class. Therefore, if you might need an accessor, you must write one now, or be able to break API. This absence of language-level property support is in sharp contrast to other modern languages, most notably C# and EcmaScript.

Strike 2 is that the JavaBeans specification defined properties as getters/setters, fields were not properties. As a result, most early enterprise frameworks supported getters/setters, but not fields. That's long in the past by now (Java Persistence API (JPA), Bean Validation, Java Architecture for XML Binding (JAXB), Jackson all support fields just fine by now), but the old tutorials and books continue to linger, and not everyone is aware that things have changed. Absence of language level property support can still be a pain in some cases (for instance because JPA lazy loading of single entities does not trigger when public fields are read), but mostly public fields work just fine. To wit, my company writes all DTOs for their REST APIs with public fields (it doesn't get more public that transmitted over the internet, after all :-).

That said, Lombok's @Data does more than generate getters/setters: It also generates toString(), hashCode() and equals(Object), which can be quite valuable.

And has encapsulation really any sense now in real life programming?

Encapsulation can be invaluable or utterly useless, it depends on the object being encapsulated. Generally, the more complex the logic within the class, the greater the benefit of encapsulation.

Automatically generated getters and setters for every field are generally overused, but can be useful to work with legacy frameworks, or to use the occasional framework feature not supported for fields.

Encapsulation can be achieved with getters and command methods. Setters are not usually appropriate, because they are expected to only change a single field, while maintaining invariants may require several fields to be changed at once.

Summary

getters/setters offer rather poor encapsulation.

The prevalence of getters/setters in Java stems from a lack of language level support for properties, and questionable design choices in its historic component model that have become enshrined in many teaching materials and the programmers taught by them.

Other object oriented languages, such as EcmaScript, do support properties at the language level, so getters can be introduced without breaking API. In such languages, getters can be introduced when you actually need them, rather than ahead of time, just-in-case-you-might-need-it-one-day, which makes for a far more pleasant programming experience.

16
  • 1
    enforces its invariants? Is it English? Could you explain it to a non-Englishman, please? Don'be too complicated - some people here do not know English well enough.
    – Gangnus
    Oct 6, 2017 at 11:43
  • I like your basis, I like your thoughts (+1), but not the practical result. I would rather use private field and some special library for loading data for them by reflection. I only don't understand why you are setting your answer as an argument against me?
    – Gangnus
    Oct 6, 2017 at 11:47
  • @Gangnus An invariant is a logical condition that doesn't change (in- meaning not and -variant meaning varies/changes). Many functions have rules that must be true before and after they execute (called pre-conditions and post-conditions) else there is an error in the code. For example a function might require that its argument not be null (a pre-condition) and might throw an exception if its argument is null because that case would be an error in the code (i.e. in computer science speak, the code has broken an invariant).
    – Pharap
    Oct 6, 2017 at 12:25
  • @Gangus: Not quite sure where reflection enters into this discussion; Lombok is an annotation processor, i.e. a plugin to the Java compiler that emits additional code during compilation. But sure, you can use lombok. I am just saying that in many cases, public fields work just as well, and are easier to set up (not all compilers detect annotations processors automatically ...).
    – meriton
    Oct 6, 2017 at 12:37
  • 1
    @Gangnus: Invariants are the same in math and CS, but in CS there is an additional perspective: From the perspective of an object, invariants must be enforced and established. From the perspective of a caller of that object, invariants are always true.
    – meriton
    Oct 8, 2017 at 21:38
7

I have asked myself that question indeed.

It's not entirely true though. The prevalence of getters/setters IMO is caused by the Java Bean specification, which requires it; so it's pretty much a feature of not Object Oriented programming but Bean oriented programming, if you will. The difference between the two is that of the abstraction layer they exist in; Beans are more of a system interface, ie on a higher layer. They abstract from the OO groundwork, or are meant to at least - as always, things are being driven too far often enough.

I'd say it's somewhat unfortunate that this Bean thing that is ubiquitous in Java programming is not accompanied by the addition of a corresponding Java language feature - I'm thinking of something like the Properties concept in C#. For those who're not aware of it, it is a language construct that looks like this:

class MyClass {
    string MyProperty { get; set; }
}

Anyway, the nitty-gritty of actual implementation still very much benefits from encapsulation.

5
  • Python has a similar feature wherein properties can have transparent getters/setters. I'm sure there are even more languages have similar features too.
    – JAB
    Oct 5, 2017 at 13:53
  • 1
    "The prevalence of getters/setters IMO is caused by the Java Bean specification, which requires it" Yes, you are right. And who said it must be required? The reason, please?
    – Gangnus
    Oct 5, 2017 at 13:55
  • 2
    The C# way is absolutely the same as that Lombok. I don't see any real difference. More practical convenience, but obviously bad in conception.
    – Gangnus
    Oct 5, 2017 at 13:56
  • 1
    @Gangnis The specification requires it. Why? Check out my answer for a concrete example of why getters are not the same as exposing fields - try achieving the same without a getter.
    – Dioxin
    Oct 5, 2017 at 14:21
  • @VinceEmigh gangnUs, please :-). (gang-walking, nus - nut). And what about using get/set only where we specifically need it, and mass loading into private fields by reflection in other cases?
    – Gangnus
    Oct 6, 2017 at 11:58
6

Could somebody explain me, what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why we do not simply use only public fields then? I feel we have walked a long and hard way only to return into the point of the start.

The simple answer here is: you are absolutely right. Getters and setters eliminate most (but not all) of the value of encapsulation. This isn't to say that any time you have a get and/or a set method that you are breaking encapsulation but if you are blindly adding accessors to all the private members of your class, you are doing it wrong.

Yes, there are other technologies that work through getters and setters. And we cannot use them through simple public fields. But these technologies appeared only because we had those numerous properties - private fields behind public getters/setters. If we had not the properties, these technologies would develop another way and would support public fields. And everything would be simple and we wouldn't have any need for Lombok now. What is the sense of the whole this cycle? And has encapsulation really any sense now in real life programming?

Getters are setters are ubiquitous in Java programming because the the JavaBean concept was pushed as a way to dynamically bind functionality to pre-built code. For example, you could have a form in an applet (anyone remember those?) which would inspect your object, find all the properties and display the as fields. Then the UI can modify those properties based on user input. You as the developer then just worry about writing the class and put any validation or business logic there etc.

enter image description here

Using Example Beans

This isn't a terrible idea per se but I've never been a big fan of the approach in Java. It's just going against the grain. Use Python, Groovy etc. Something that supports this kind of approach more naturally.

The JavaBean thing kind of spun out of control because it created JOBOL i.e. Java written developers that don't understand OO. Basically, objects became nothing more than bags of data and all the logic was written outside in long methods. Because it was seen as normal, people like you and me who question this were considered kooks. Lately I've seen a shift and this isn't as much of an outsider position

The XML binding thing is a tough nut. This is probably not a good battlefield for making a stand against JavaBeans. If you have to build these JavaBeans, try to keep them out of the real code. Treat them as part of the serialization layer.

10
  • 2
    The most concrete and wise thoughts. +1. But why not to write a group of classes, of which every one will massively load/save private fields to/from some external structure? JSON, XML, other object. It could work with POJOs or, maybe, only with fields, marked by an annotation for that. Now I am thinking about that alternative.
    – Gangnus
    Oct 6, 2017 at 11:54
  • @Gangnus At a guess because that means a lot of extra code writing. If reflection is used then only one lump of serialisation code has to be written and it can serialise any class that follows the specific pattern. C# supports this through the [Serializable] attribute and its relatives. Why write 101 specific serialisation methods/classes when you can just write one by leveraging reflection?
    – Pharap
    Oct 6, 2017 at 12:30
  • @Pharap I am terribly sorry, but your comment is too short for me. "leveraging reflection"? And what is the sense of hiding so different things into one class? Could you explain what you mean?
    – Gangnus
    Oct 6, 2017 at 12:42
  • @Gangnus I mean reflection, the ability for code to inspect its own structure. In Java (and other languages) you can get a list of all the functions a class exposes and then use those as a way of extracting the data from the class required to serialise it to e.g. XML/JSON. Using reflection that would be a one-time job instead of constantly making new methods to save structures on a per-class basis. Here's a simple Java example.
    – Pharap
    Oct 6, 2017 at 12:47
  • @Pharap That is just what I was talking about. But why you call it "leveraging"? And the alternative I mentioned in first comment here remains - should (un)packed fields have special annotations or not?
    – Gangnus
    Oct 6, 2017 at 12:56
6

How much can we get done without getters? Is it possible to completely remove them? What problems does this create? Could we even ban the return keyword?

It turns out you can do a lot if you're willing to do the work. How then does information ever get out of this fully encapsulated object? Through a collaborator.

Rather than let code ask you questions you tell things to do things. If those things also don't return things you don't have to do anything about what they return. So when you're thinking of reaching for a return instead try reaching for some output port collaborator that will do whatever is left to be done.

Doing things this way has benefits and consequences. You have to think about more than just what you would have returned. You have to think about how you're going to send that as a message to an object that didn't ask for it. It might be you pass out the same object you would have returned, or it could be simply calling a method is enough. Doing that thinking comes at a cost.

The benefit is that now you're doing your talking facing the interface. This means you get the full benefit of abstraction.

It also gives you polymorphic dispatch, because while you know what you're saying you don't have to know what exactly you're saying it to.

You might think this means you have to go only one way through a stack of layers, but it turns out you can use polymorphism to go backwards without creating crazy cyclic dependencies.

It might look like this:

Enter image description here

class Interactor implements InputPort {
    OutputPort out;
    int y = 0;

    Interactor(OutputPort out){
        this.out = out;
    }

    void accumulate(int x) {
        y = y + x;
        out.showAsImage(y);
    }
}

If you can code like that then using getters is a choice. Not a necessary evil.

2
  • Yes, such getters/setters have great sense. But do you understand it is about something else? :-) +1 anyway.
    – Gangnus
    Oct 6, 2017 at 12:05
  • 1
    @Gangnus Thank you. There are a bunch of things you could be talking about. If you'd care to at least name them I could go into them. Oct 6, 2017 at 12:18
6

This is a controversial issue (as you can see) because a bunch of dogma and misunderstandings is mixed up with the reasonable concerns regarding the issue of getters and setters. But in short, there is nothing wrong with @Data and it does not break encapsulation.

Why use getters and setters rather than public fields?

Because getters/setters provide encapsulation. If you expose a value as a public field and then later change to calculating the value on the fly, then you need to modify all clients accessing the field. Clearly this is bad. It is an implementation detail whether some property of an object is stored in a field, is generated on the fly or is fetched from somewhere else, so the difference should not be exposed to clients. Getter/setters setter solve this, since they hide the implementation.

But if the getter/setter just reflect an underlying private field isn't it just as bad?

No! The point is the encapsulation allows you to change the implementation without affecting clients. A field might still be a perfectly fine way to store value, as long as clients does not have to know or care.

But isn't autogenerating getters/setters from fields breaking encapsulation?

No the encapsulation is still there! @Data annotations are just a convenient way to write getter/setter-pairs which uses an underlying field. For the viewpoint of a client this is just like a regular getter/setter pair. If you decide to rewrite the implementation you can still do it without affecting the client. So you get the best of both worlds: encapsulation and concise syntax.

But some say getter/setters are always bad!

There is a separate controversy, where some believe the getter/setter pattern is always bad, regardless of the underlying implementation. The idea is that you should not set or get values from objects, rather any interaction between objects should be modeled as messages where one object asks another object to do something. This is mostly a piece of dogma from the early days of object-oriented thinking. The thinking now is that for certain patterns (e.g. value objects, data transfer object) getters/setters may be perfectly appropriate.

11
  • Encapsulation should allow us polymorphism and safety. Gets/sets allow the firs, but are strongly against the second.
    – Gangnus
    Oct 6, 2017 at 13:06
  • If you say I use a dogma somewhere, please, say, what of my text is dogma. And don't forget, that some thoughts I am using I do not like or agree with. I am using them for demonstration of a contradiction.
    – Gangnus
    Oct 6, 2017 at 13:39
  • 1
    "GangNus" :-). A week ago I have worked on a project literally full of calling getters and setters across several layers. It is absolutely unsafe, and even worse, it supports people in unsafe coding. I am glad I have changed jobs fast enough, for people become accustomed to that manner. So, I do not think it, I see it.
    – Gangnus
    Oct 6, 2017 at 19:40
  • 2
    @jrh: I don't know if there is a formal explanation as such, but C# have built-in support for properties (getters/setters with a nicer syntax) and for anonymous types which are types with only properties and no behavior. So the designers of C# have deliberately diverged from the the messaging metaphor and "tell, don't ask" principle. The development of C# since version 2.0 seem to have more inspired by functional languages than by traditional OO purity.
    – JacquesB
    Oct 6, 2017 at 22:21
  • 1
    @jrh: I'm guessing now, but I think C# is quite inspired by functional languages where data and operations are more separate. In distributed architectures, the perspective have also shifted from message-oriented (RPC, SOAP) to data-oriented (REST). And databases which expose and operate on data (not "black box" objects) have prevailed. In short I think focus have shifted from messages between black boxes to exposed data models
    – JacquesB
    Oct 7, 2017 at 8:44
5

Encapsulation does have a purpose, but it can also be misused or abused.

Consider something like the Android API which has classes with dozens (if not hundreds) of fields. Exposing those fields the consumer of the API makes it harder to navigate and use, also it gives the user the false notion that he can do whatever he wants with those fields which may conflict with how they are supposed to be used. So encapsulation is great in that sense for maintainability, usability, readability and avoiding crazy bugs.

On the other hand, POD or plain old data types, like a struct from C/C++ in which all the fields are public can be useful as well. Having useless getters/setters like the ones generated by the @data annotation in Lombok is just a way to keep the "Encapsulation pattern". One of the few reason we do "useless" getters/setters in Java is that the methods provide a contract.

In Java, you can not have fields in an interface, therefore you use getters and setters to specify a common property that all implementers of that interface have. In more recent languages like Kotlin or C# we see the concept of properties as fields for which you can declare a setter and getter. In the end, useless getters/setters are more of a legacy that Java has to live with, unless Oracle adds properties to it. Kotlin, for example, which is another JVM language developed by JetBrains, has data classes which basically do what the @data annotation does in Lombok.

Also here are a few examples:

class DataClass 
{
    private int data;

    public int getData() { return data; }
    public void setData(int data) { this.data = data; } 
}

This is a bad case of encapsulation. The getter and setter are effectively useless. Encapsulation is mostly used because this is the standard in languages like Java. Does not actually help, besides maintaining consistency across the code base.

class DataClass implements IDataInterface
{
    private int data;

    @Override public int getData() { return data; }
    @Override public void setData(int data) { this.data = data; }
}

This is a good example of encapsulation. Encapsulation is used to enforce a contract, in this case IDataInterface. The purpose of encapsulation in this example is to make the consumer of this class use the methods that are provided by the interface. Even though the getter and setter don't do anything fancy, we have now defined a common trait between DataClass and other implementers of IDataInterface. Thus I can have a method like this:

void doSomethingWithData(IDataInterface data) { data.setData(...); }

Now, when talking about encapsulation I think its important to also adress the syntax problem. I often see people complain about the syntax that is necessary to enforce encapsulation rather than encapsulation itself. One example that comes to mind is from Casey Muratori (you can see his rant here).

Suppose you have a player class that uses encapsulation and want to move his position by 1 unit. The code would look like this:

player.setPosX(player.getPosX() + 1);

Without encapsulation it would look like this:

player.posX++;

Here he argues that encapsulations leads to a lot more typing with no added benefits and this can in many cases be true, but notice something. The argument is against the syntax, not encapsulation itself. Even in languages like C that lack the concept of encapsulation you will often see variables in structs prexifed or sufixed with '_' or 'my' or whatever to signify that they should not be used by the consumer of the API, as if they were private.

The fact of the matter is encapsulation can help make code much more maintainable and easy to use. Consider this class:

class VerticalList implements ...
{
    private int posX;
    private int posY;
    ... //other members

    public void setPosition(int posX, int posY)
    {
        //change position and move all the objects in the list as well
    }
}

If the variables were public in this example then a consumer of this API would be confused as to when to use posX and posY and when to use setPosition(). By hiding those details you help the consumer better use your API in an intuitive way.

The syntax is a limitation in many languages though. However newer languages offer properties which gives us the nice syntax of publice members and the benefits of encapsulation. You will find properties in C#, Kotlin, even in C++ if you use MSVC. here is an example in Kotlin.

class VerticalList : ... { var posX: Int set(x) { field = x; ... } var posY: Int set(y) { field = y; ... } }

Here we achieved the same thing as in the Java example, but we can use posX and posY as if they were public variables. When I try to change their value though, the body of the settter set() will be executed.

In Kotlin for example, this would be the equivalent of a Java Bean with getters, setters, hashcode, equals and toString implemented:

data class DataClass(var data: Int)

Notice how this syntax allows us to do a Java Bean in one line. You correctly noticed the problem that a language like Java has in implementing encapsulation, but that is the fault of Java not of encapsulation itself.

You said that you use Lombok's @Data to generate getters and setters. Notice the name, @Data. It's mostly meant to be used on data classes that only store data and are meant to be serialized and deserialized. Think of something like a save file from a game. But in other scenarios, like with a UI element, you most definetely want setters because just changing the value of a variable may not be enough to get the expected behaviour.

3
  • Could you put here an example about misuse of encapsulation? That could be interesting. Your example is rather against the lack of encapsulation.
    – Gangnus
    Oct 6, 2017 at 12:25
  • 1
    @Gangnus I added a few examples. Useless encapsulation is generally misuse and also from my experience API developers try too hard to force you to use an API in a certain way. I didn't put an example for that because I didn't have an easy one to present. If I find one I'll definetely add it. I think most comments against encapsulation are actually against the syntax of encapsulation rather than encapsulation itself.
    – BananyaDev
    Oct 7, 2017 at 13:01
  • Thanks for the edit. I found a minor typo though: "publice" instead of "public".
    – jrh
    Oct 7, 2017 at 13:25
4

I will try to illustrate the problem space of encapsulation and class design, and answer your question at the end.

As mentioned in other answers, the purpose of encapsulation is to hide internal details of an object behind a public API, which serves as a contract. The object is safe to change its internals because it knows it is only called through the public API.

Whether it makes sense to have public fields, or getters/setters, or higher-level transaction methods or message passing depends on the nature of the domain that is being modeled. In the book Akka Concurrency (which I can recommend even if it is somewhat out of date) you find an example that illustrates this, which I'll abbreviate here.

Consider a User class:

public class User {
  private String first = "";
  private String last = "";

  public String getFirstName() {
    return this.first;
  }
  public void setFirstName(String s) {
    this.first = s;
  }

  public String getLastName() {
    return this.last;
  }
  public void setLastName(String s) {
    this.last = s;
  }
}

This works fine in a single-threaded context. The domain being modeled is a person's name, and the mechanics of how that name is stored can be perfectly encapsulated by the setters.

However, imagine this must be provided in a multi-threaded context. Suppose one thread is periodically reading the name:

System.out.println(user.getFirstName() + " " + user.getLastName());

And two other threads are fighting a tug of war, setting it to Hillary Clinton and Donald Trump in turn. They each need to call two methods. Mostly this works fine, but every once in a while you're going to see a Hillary Trump or a Donald Clinton pass by.

You cannot solve this problem by adding a lock inside the setters, because the lock is only held for the duration of setting either the first name or the last name. The only solution through locking is to add a lock around the entire object, but that breaks encapsulation since the calling code must manage the lock (and may cause deadlocks).

As it turns out, there is no clean solution through locking. The clean solution is to encapsulate the internals again by making them more coarse:

public class UserName {
   public final String first;
   public final String last;
   public UserName(String first, String last) { ... }
}

public class User
   private UserName name;
   public UserName getName() { return this.name; }
   public setName(UserName n) { this.name = n; }
}

The name itself has become immutable, and you see that its members can be public, because it is now a pure data object with no ability to modify it once created. In turn, the User class's public API has become more coarse, with only a single setter left, so that the name can only be changed as a whole. It encapsulates more of its internal state behind the API.

What is the sense of the whole this cycle? And has encapsulation really any sense now in real life programming?

What you see in this cycle are attempts to apply solutions good for a specific set of circumstances too broadly. A suitable level of encapsulation requires understanding the domain being modeled and applying the right level of encapsulation. Sometimes this means all fields are public, sometimes (like in Akka applications) it means you have no public API at all except for a single method to receive messages. However, the concept of encapsulation itself, meaning the hiding of internals behind a stable API, is key to programming software at scale, especially in multi-threaded systems.

3
  • Fantastic. I would say that you raised the discussion a level higher. Maybe two. Really, I thought that I understand encapsulation and sometimes better that standard books, and was really rather afraid that we are practically losing it due to some accepted custom and technologies, and THAT was the real subject of my question (maybe, not formulated in a good way), but you have shown me really new sides of the encapsulation. I am definitely not good in multitasking and you have shown me how harmful it could be for understanding other basic principles.
    – Gangnus
    Oct 10, 2017 at 7:43
  • But I can't mark your post as the answer, for it is not about mass loading data to/from objects, the problem due to which such platforms as beans and Lombok appear. Maybe, you could elaborate your thoughts in that direction, please, if you can get time? I myself have yet to rethink the consequences your thought brings to that problem. And I am not sure I am fit enough for it (remember, bad multithreading background :-[ )
    – Gangnus
    Oct 10, 2017 at 7:48
  • I haven't used lombok, but as I understand it is a way of implementing a specific level of encapsulation (getters/setters on every field) with less typing. It doesn't change the ideal shape of the API for your problem domain, it's just a tool to more quickly write it. Oct 10, 2017 at 7:51
2

Encapsulation gives you flexibility. By separating structure and interface, it allows you to change the structure without changing the interface.

E.g. if you find that you need to compute a property based on other fields instead of initializing the underlying field on construction, you can simply change the getter. If you had exposed the field directly, you'd instead have to change the interface and make changes at every usage site.

7
  • While a good idea in theory, remember that causing version 2 of an API to throw exceptions that version 1 didn't would break users of your library or class terribly (and possibly silently!); I am a bit skeptical that any major change could be made "behind the scenes" on most of these data classes.
    – jrh
    Oct 5, 2017 at 13:34
  • Sorry, it is not an explanation. The fact that using fields is forbidden in interfaces comes from encapsulation idea. And now we are talking about it. You are basing on the facts being discussed. (notice, I am not talking pro or contra your thoughts, only about they cannot be used as arguments here)
    – Gangnus
    Oct 5, 2017 at 13:54
  • @Gangnus The word "interface" has meaning outside of just the Java keyword: dictionary.com/browse/interface
    – glennsl
    Oct 5, 2017 at 14:17
  • 1
    @glennsl I'm not arguing against encapsulation (I think it can be a good thing), and when an object is designed with flexibility in mind, you are absolutely right. I'm saying that the sort of classes the OP is talking about (where all of the state of the object is exposed) don't leave any room for interpretation or change on the object's part because they have no encapsulation at all, ergo there is no room for the behavior to object to differ, because the object has no behavior, only data.
    – jrh
    Oct 5, 2017 at 14:50
  • 1
    The old encapsulation, without mass get/set gave us not only Polymorphism, but also the safety. And mass sets/gets teach us towards the bad practice.
    – Gangnus
    Oct 6, 2017 at 11:32
1

I can think of one use case where this makes sense. You might have a class that originally you access through a simple getter/setter API. You later extend or modify so that it no longer uses the same fields, but still supports the same API.

A somewhat contrived example: a Point that starts out as a Cartesian pair with p.x() and p.y(). You later make a new implementation or subclass that uses polar coordinates, so you can also call p.r() and p.theta(), but your client code that calls p.x() and p.y() remains valid. The class itself transparently converts from the internal polar form, that is, y() would now return r * sin(theta);. (In this example, setting only x() or y() does not make as much sense, but still is possible.)

In this case, you might find yourself saying, “Glad I bothered automatically declaring getters and setters instead of making the fields public, or I’d have had to break my API there.”

4
  • 2
    It is not so nice as you see it. Changing the inner physical representation really makes the object different. And it is bad that they look the same way, for they have different qualities. Descartes system has no special points, the polar system has one.
    – Gangnus
    Oct 6, 2017 at 11:40
  • 1
    If you don’t like that specific example, surely you can think of others that really do have multiple equivalent representations, or a newer representation which can be converted to and from an older one.
    – Davislor
    Oct 6, 2017 at 12:06
  • Yes, you can very productively use them for presentation. In UI it can be used widely. But aren't you making me to answer my own question ? :-)
    – Gangnus
    Oct 6, 2017 at 12:10
  • More efficient that way, isn’t it? :)
    – Davislor
    Oct 6, 2017 at 18:09
0

I'm not actually a Java developer; but the following is pretty much platform agnostic.

Just about everything we write uses public getters and setters that access private variables. Most of the getters and setters are trivial. But when we decide that the setter needs to recalculate something or the setter does some validation or we need to forward the property to a property of a member variable of this class this is completely non-breaking to the entire code and is binary compatible so we can swap that one module out.

When we decide this property really should be computed on the fly all the code that looks at it doesn't have to change and only the code that writes to it needs to change and the IDE can find it for us. When we decide that it's a writable computed field (only ever had to do that a few times) we can do that too. The nice thing is quite a few of these changes are binary compatible (changing to the read-only computed field is not in theory but might be in practice anyway).

We've ended up with lots and lots of trivial getters with complicated setters. We also ended up with quite a few caching getters. The end result is you are allowed to assume that getters are reasonably cheap but setters might not be. On the other hand, we're wise enough to decide that setters don't persist to disk.

But I had to track down the guy who was blindly changing all member variables to properties. He didn't know what atomic add was so he changed the thing that really did need to be a public variable to a property and broke the code in a subtle way.

2
  • Welcome to the Java world. (C#, too). *sigh*. But as for using get/set for hiding inner representation, be careful. It it is about external format only, it is OK. But if it is about maths, or, worse, physics or other real thing, different inner representation has different qualities. viz my comment to softwareengineering.stackexchange.com/a/358662/44104
    – Gangnus
    Oct 6, 2017 at 12:11
  • @Gangnus: That point example is most unfortunate. We've had quite a few of these but the calculation was always exact.
    – Joshua
    Oct 6, 2017 at 15:19
-1

Getters and setters are a "just in case" measure added for the sake of avoiding refactoring in the future if the internal structure or the access requirements change during the development process.

Say, a few months after release, your client informs you that one of the fields of a class is sometimes set to negative values, even though it should clamp to 0 at most in that case. Using public fields, you would have to search for every assignment of this field in your whole codebase to apply the clamping function to the value you are going to set, and keep in mind that you will always have to do that when modifying this field, but that sucks. Instead, if you happened to be using getters and setter already, you would have been able to just modify your setField() method to ensure this clamping is always applied.

Now, the problem with "antiquated" languages like Java is that they encourage using methods for this purpose, which just makes your code infinitely more verbose. It's painful to write, and difficult to read, which is why we have been using IDE that mitigate this problem in one way or another. Most IDE will automatically generate getters and setters for you, and also hide them unless told otherwise. Lombok goes a step further and simply generates them procedurally during compile time to keep your code extra sleek. However, other more modern languages have simply solved this issue in one way or another. Scala or .NET languages, for example, allow you to safely ignore method whether you will actually need getters and setters in the future due to built-in solutions that let you keep the external interface/contract/API/facade/whatever while changing your internals.

For example, in VB .NET or C#, you could simply make all fields meant to have plain -no side effect- getters and setters just public fields, and later make them private, change their name and expose a Property with the previous name of the field, where you can fine tune the access behaviour of the field, if you need it. With Lombok, if you need to fine tune the behaviour of a getter or setter, you could simply remove those tags when needed, and code your own with the new requirements, knowing for sure you will have to refactor nothing in other files.

Basically, the way your method accesses a field should be transparent and uniform. Modern languages let you define "methods" with the same access/call syntax of a field, so these modifications can be done on demand without thinking much about it during early development, but Java forces you to do this work ahead of time because it does not have this feature. All Lombok does is saving you some time, because the language you are using didn't want to let you save time for a "just in case" method.

2
  • In those "modern" languages, the API looks like a field access foo.bar but it may get handled by a method call. You claim this is superior to the "antiquated" way of having an API look like method calls foo.getBar(). We seem to agree that public fields are problematic, but I claim that the "antiquated" alternative is superior to the "modern" one, since our whole API is symmetric (it's all method calls). In the "modern" approach, we have to decide which things should be properties and which should be methods, which overcomplicates everything (especially if we use reflection!).
    – Warbo
    Oct 5, 2017 at 17:05
  • 1
    1. Hiding physics is not always so good. look my comment to softwareengineering.stackexchange.com/a/358662/44104. 2. I am not talking about how hard or simple is the getter/setter creation. The C# has absolutely the same problem we are talking about. The lack of incapsulation due to the bad solution of mass info loading. 3. Yes, the solution could be based on syntax, but, please, in some different ways than you are mentioning. Those are bad, we have here a large page filled by explanations. 4. The solution needn't be based on syntax. It could be done in Java limits.
    – Gangnus
    Oct 6, 2017 at 12:32
-1

Could somebody explain me, what is the sense of hiding all fields as private and after that to expose all of them by some extra technology?

There is absolutely no point. However, the fact you ask that question demonstrates that you have not understood what Lombok does, and that you do not understand how to write OO code with encapsulation. Let's rewind a bit...

Some data for a class instance will always be internal, and should never be exposed. Some data for a class instance will need to be set externally, and some data may need to be passed back out of a class instance. We may want to change how the class does stuff under the surface though, so we use functions to allow us to get and set data.

Some programs want to save state for class instances, so these may have some serialisation interface. We add more functions which lets the class instance store its state to storage and retrieve its state from storage. This maintains encapsulation because the class instance is still in control of its own data. We may be serialising private data, but the rest of the program doesn't have access to it (or more accurately, we maintain a Chinese Wall by choosing not to deliberately corrupt that private data), and the class instance can (and should) conduct integrity checks on deserialisation to make sure its data came back OK.

Sometimes the data needs range checks, integrity checks, or stuff like that. Writing these functions ourselves lets us do all that. In this case we don't want or need Lombok, because we're doing all that ourselves.

Frequently though, you find that an externally-set parameter is stored in a single variable. In this case you would need four functions to get/set/serialise/deserialise the contents of that variable. Writing these four functions yourself every time slows you down and is prone to errors. Automating the process with Lombok speeds up your development and removes the possibility of errors.

Yes, it would be possible to make that variable public. In this particular version of code, it would be functionally identical. But go back to why we're using functions: "We may want to change how the class does stuff under the surface..." If you make your variable public, you are constraining your code now and forever to have this public variable as the interface. If you use functions though, or if you use Lombok to automatically generate those functions for you, you are free to change the underlying data and the underlying implementation at any time in the future.

Does this make it clearer?

1
  • You are talking about the questions of the first-year SW student. We are elsewhere already. I would never say this and even would give you a plus for clever answers, if you were not so impolite in the form of your answer.
    – Gangnus
    Oct 6, 2017 at 12:34
-1

Could somebody explain to me what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why do we simply not use only public fields then? I feel we have walked a long and hard way only to return to the starting point.

Yeah, it is contradictory. I first ran into properties in Visual Basic. Up to then, in other languages, my experience, there were no property wrappers around fields. Just public, private, and protected fields.

Properties was a kind of encapsulation. I understood Visual Basic properties to be a way to control and manipulate output of one or more fields while hiding the explicit field and even its data type, for example emitting a date as a string in a particular format. But even then one is not "hiding state and exposing functionality" from the larger object perspective.

But properties justify themselves because of separate property getters and setters. Exposing a field without a property was all or nothing - if you could read it you could change it. So now one can justify weak class design with protected setters.

So why didn't we/they just use actual methods? Because it was Visual Basic (and VBScript) (oooh! aaah!), coding for the masses (!), and it was all the rage. And thus an idiotocracy eventually dominated.

4
  • Yes, properties for UI have special sense, they are public and pretty representations of the fields. Good point.
    – Gangnus
    Oct 6, 2017 at 12:03
  • "So why didn't we/they just use actual methods?" Technically they did in the .Net version. Properties are syntactic sugar for get and set functions.
    – Pharap
    Oct 6, 2017 at 12:43
  • "idiotocracy"? Don't you mean "idiosyncrasy"? Oct 6, 2017 at 19:35
  • I meant idiocracy
    – radarbob
    Oct 7, 2017 at 4:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.