简体   繁体   English

不可变类型:公共 final 字段与 getter

[英]Immutable Type: public final fields vs. getter

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:由于 String 本身是一种不可变类型,我想到了这样的事情:

public final class Immu
{
  public final String foo;
  public final String bar;

  public Immu(final String foo, final String bar)
  {
    this.foo = foo;
    this.bar = bar;
  }
}

Many people seem to object using public fields at all and use Getters instead.许多人似乎完全反对使用公共字段,而是使用 Getters。 IMHO this would be just boilerplate in this case, because String itself is immutable.恕我直言,在这种情况下,这只是样板文件,因为 String 本身是不可变的。

Other thoughts I may be missing on this one?我可能在这个问题上遗漏了其他想法?

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. esp 包本地类。 then I would avoid getter/setters and use package local or public fields.那么我会避免使用 getter/setter 并使用包本地或公共字段。

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.如果您希望其他模块/开发人员使用一个类,那么从长远来看,遵循 getter/setter 模型可能是一种更安全的方法。

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.您稍后可能需要修改foo以便它通过方法获得而不是被修复,并且如果您公开该字段而不是 getter,您将需要破坏您的 API。

This answer is obviated:这个答案被排除了:

Why not为什么不

interface Immu { String getA() ; String getB ( ) }

Immu immu ( final String a , final String b )
{
       /* validation of a and b */
       return new Immu ( )
       {
              public String getA ( ) { return a ; }

              public String getB ( ) { return b ; }
       }
}

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. public final对于不可变类型看起来最干净。
  • 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 (eg for LMAX Disruptor).如果您需要工厂方法(例如 LMAX Disruptor),这很重要。 In a similar way instantiating your objects via reflection becomes more complicated.以类似的方式,通过反射实例化对象变得更加复杂。
  • Getters and setters can have side effects. getter 和 setter 可能有副作用。 Using public final clearly tells the programmer that no hidden magic is occuring and the object is inherently dumb :)使用public final清楚地告诉程序员没有隐藏的魔法发生并且对象本质上是愚蠢的:)
  • 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.如果您处于开发中期并且没有指南阻止您并且项目是孤立的,或者您可以控制所有涉及的项目,我建议对不可变类型使用public final If you decide you need getters later on, Eclipse offers Refactor -> Encapsulate Field... which automatically creates these and adjusts all references to the field.如果您决定稍后需要 getter,Eclipse 提供Refactor -> Encapsulate Field...它会自动创建这些并调整对该字段的所有引用。

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.我在家庭项目中使用 public-final-field (anti?) 模式,这些类基本上是一个带有构造函数的不可变数据结构,以及绝对基础,如 equals()、hashCode()、toString() 等,如果需要的话. (I'm avoiding the word "struct" because of the various different language interpretations of it.) (我避免使用“struct”这个词,因为它有各种不同的语言解释。)

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.我不会将这种方法引入其他人的代码库(工作、公共项目等),因为它可能与其他代码不一致,并且优先考虑 When In Rome 或 Least Surprise 等原则。

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.也就是说,关于 Daniel C. Sobral 和 aioobe 的回答,我的态度是,如果类设计由于不可预见的发展而成为问题,那么在 IDE 中需要 30 秒的时间来私有化字段并添加访问器,仅此而已除非有数百个引用,否则修复损坏的引用需要 5 或 10 分钟以上。 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.] [编辑:Effective Java 坚决反对这个想法,同时指出它在不可变字段上“危害较小”。]

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.如果您正在尝试编写好的 Java 代码,我建议您只使用 getter,因为它对 Java 友好,最重要的是,它可以节省大量时间搜索原因。

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.但是您必须花时间说服自己(或您的代码审查者)为什么您可以或应该反抗 Java 正统观念。

It is better to just write the getter/setter and spent the time for something more useful: like complaining java最好只编写 getter/setter 并花时间做一些更有用的事情:比如抱怨 java

It is not very clear if someone is going to use your code through an API.不清楚是否有人会通过 API 使用您的代码。 You are also missing an opportunity to validate the input, if you are going to require some later.如果您稍后需要一些验证输入,您也将错过验证输入的机会。

Using public final may be fine for such small job , but it cannot be adapted as a standard practice ,对于这么小的工作使用 public final 可能没问题,但它不能作为标准做法进行调整

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.这是一个巨大的安全风险,当然像 SONAR 这样的工具可以提前捕获它。 But its manageable only if you are using getter-setters.但是只有当您使用 getter-setter 时它才可以管理。

If you are using getters, you can very well write如果您正在使用 getter,则可以很好地编写

   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.看看上面的例子,如果你使用数组,强烈建议不要使用public final 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?那你呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM