Immutable Type: public final fields vs. getter

前端 未结 8 848
臣服心动
臣服心动 2020-11-30 04:48

I need a small Container-Class for storing some Strings which should be immutable. As String itself is an immutable type, I thought of something like that:

p         


        
相关标签:
8条回答
  • 2020-11-30 04:51

    I use the public-final-field (anti?)pattern on home projects for classes which are basically an immutable data structure with a constructor, along with absolute basics like equals(), hashCode(), toString(), etc. if required. (I'm avoiding the word "struct" because of the various different language interpretations of it.)

    I wouldn't bring this approach to someone else's codebase (work, public project, etc) because it would likely be inconsistent with other code, and principles like When In Rome or Least Surprise take priority.

    That said, with regard to Daniel C. Sobral's and aioobe's answers, my attitude is that if the class design becomes a problem because of unforeseen developments, it's the work of 30 seconds in an IDE to privatise the fields and add accessors, and no more than 5 or 10 minutes to fix broken references unless there are hundreds of them. Anything that fails as a result gets the unit test it should have had in the first place.:-)

    [Edit: Effective Java is quite firmly against the idea, while noting that it's "less harmful" on immutable fields.]

    0 讨论(0)
  • 2020-11-30 05:01

    I would do what you believe is simplest and clearest. If you have a data value class which is only used by a restricted number of classes. esp a package local class. then I would avoid getter/setters and use package local or public fields.

    If you have a class which you expect other modules/developers to use, following a getter/setter model may be a safer approach in the long run.

    0 讨论(0)
  • 2020-11-30 05:08

    Forget about encapsulation, immutability, optimization and all other big words. If you are trying to write good java code, I would recommend you just use getter simply because it is java friendly, and most importantly it saves ton of time googling why.

    For example, you probably would not expect using streams when you write the code, but later you found

    listOfImmus.stream().map(immu -> imm.foo).collect(Collectors.toSet()); // with field
    listOfImmus.stream().map(Immu::getFoo).collect(Collectors.toSet());    // with getter
    
    Supplier<String> s = () -> immu.foo;  // with field
    Supplier<String> s = immu::foo; // with getter
    
    // final fields are hard to mock if not impossible. 
    Mockito.when(immuMock.getFoo()).thenReturn("what ever");
    
    //one day, your code is used in a java Beans which requires setter getter..
    ¯\_(ツ)_/¯
    
    

    This list can be long or short or may be none of them makes any sense to your use case. But you have to spend time convincing yourself (or your code reviewers) why you can or should rebel against java orthodoxy.

    It is better to just write the getter/setter and spent the time for something more useful: like complaining java

    0 讨论(0)
  • 2020-11-30 05:11

    Using public final may be fine for such small job, but it cannot be adapted as a standard practice,

    Consider the situation below.

    Public class Portfolio {
       public final String[] stocks;
    }
    

    Of course, being immutable, this object is initialized vis constructor, and then accessed directly. Do I have to tell you the problem in it? It’s evident!

    Consider your client writing the code like below -

    Portfolio portfolio = PortfolioManager.get(“Anand”);
    Portfolio.stocks[0] = “FB”;
    portfolio.calculate();
    

    Is this doable? Your client libraries are able to manipulate the state of your objects, or rather able to hack within your runtime representation. This is a huge security risk, and of course tools like SONAR catch it upfront. But its manageable only if you are using getter-setters.

    If you are using getters, you can very well write

       Public class Portfolio {
          private final String[] stocks;
          public String[] getStocks() {
              return Arrays.coptOf(this.stocks);
          }
       }
    

    This prevents you from potential security threat.

    Looking at the above example, using public final is strongly discouraged if you are using arrays. In such case, it cannot become a standard. A person like me, will refrain from using a code practice that cannot become a uniform standard across all data types. What about you?

    0 讨论(0)
  • 2020-11-30 05:12

    I found this thread hoping for some actual arguments, but the answers I've seen here didn't help me all that much. After some more research and thinking I think the following has to be considered:

    • public final looks cleanest for immutable types.
    • Mutable types could be altered by accessors even if this is not intended - in concurrent environments this could lead to a lot of headaches.
    • There can be no no-arguments constructor. This is importent if you need factory methods (e.g. for LMAX Disruptor). In a similar way instantiating your objects via reflection becomes more complicated.
    • Getters and setters can have side effects. Using public final clearly tells the programmer that no hidden magic is occuring and the object is inherently dumb :)
    • You can't return a wrapper or a derived class instance to the accessor. Then again, this is something you should know about when the field is assigned its value. In my opinion container classes should not be concerned about what to return to whom.

    If you're mid development and no guideline is stopping you and the project is isolated or you have control over all involved projects I'd suggest using public final for immutable types. If you decide you need getters later on, Eclipse offers Refactor -> Encapsulate Field... which automatically creates these and adjusts all references to the field.

    0 讨论(0)
  • 2020-11-30 05:14

    The problem is the uniform access principle. You may later need to modify foo so that it's obtained through a method instead of being fixed, and if you exposed the field instead of a getter, you'll need to break your API.

    0 讨论(0)
提交回复
热议问题