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

发布于 2024-11-27 23:36:59 字数 360 浏览 0 评论 0原文

我需要一个小的容器类来存储一些应该不可变的字符串。由于 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;
  }
}

许多人似乎根本反对使用公共字段并使用 Getters 代替。恕我直言,在这种情况下这只是样板,因为字符串本身是不可变的。

我对此可能缺少其他想法吗?

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:

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. IMHO this would be just boilerplate in this case, because String itself is immutable.

Other thoughts I may be missing on this one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

小嗲 2024-12-04 23:36:59

我会做你认为最简单、最清晰的事情。如果您有一个仅由有限数量的类使用的数据值类。 esp 是一个包本地类。那么我会避免 getter/setter 并使用包本地或公共字段。

如果您有一个希望其他模块/开发人员使用的类,从长远来看,遵循 getter/setter 模型可能是更安全的方法。

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.

弥繁 2024-12-04 23:36:59

问题在于统一访问原则。您稍后可能需要修改 foo ,以便通过方法获取而不是修复它,并且如果您公开该字段而不是 getter,则需要破坏您的 API。

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.

暖阳 2024-12-04 23:36:59

这个答案被排除了:

为什么不呢?

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 ; }
       }
}

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 ; }
       }
}
一萌ing 2024-12-04 23:36:59

我发现这个帖子希望得到一些实际的论点,但我在这里看到的答案对我没有多大帮助。经过更多的研究和思考,我认为必须考虑以下几点:

  • 对于不可变类型来说,public final 看起来最干净。
  • 可变类型可以被访问器改变,即使这不是有意的——在并发环境中,这可能会导致很多麻烦。
  • 不能有无参数构造函数。如果您需要工厂方法(例如 LMAX Disruptor),这一点很重要。以类似的方式,通过反射实例化对象变得更加复杂。
  • Getter 和 Setter 可能会产生副作用。使用 public final 清楚地告诉程序员没有隐藏的魔法发生,并且该对象本质上是愚蠢的:)
  • 您不能将包装器或派生类实例返回给访问器。话又说回来,当为字段分配值时,您应该了解这一点。在我看来,容器类不应该关心什么返回给谁。

如果您处于开发中期,并且没有任何指导方针阻止您,并且项目是孤立的,或者您可以控制所有涉及的项目,我建议对不可变类型使用 public final 。如果您稍后决定需要 getter,Eclipse 会提供 Refactor -> Encapsulate Field... 自动创建这些字段并调整对该字段的所有引用。

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.

苄①跕圉湢 2024-12-04 23:36:59

我在家庭项目中使用 public-final-field (反?)模式,这些类基本上是带有构造函数的不可变数据结构,以及绝对基础知识,如 equals()、hashCode()、toString() 等(如果需要) 。 (我避免使用“结构”这个词,因为它有各种不同的语言解释。)

我不会将这种方法带到其他人的代码库(工作、公共项目等)中,因为它可能与其他代码不一致,优先考虑“在罗马时”或“最少惊喜”等原则。

也就是说,对于 Daniel C. Sobral 和 aioobe 的回答,我的态度是,如果类设计由于不可预见的发展而成为问题,那么只需在 IDE 中花费 30 秒即可将字段私有化并添加访问器,仅此而已除非有数百个引用,否则修复损坏的引用的时间不会超过 5 或 10 分钟。任何失败的结果都会得到它应该首先进行的单元测试。:-)

[编辑:Effective Java 非常坚决地反对这个想法,同时指出它对不可变字段“危害较小”。]

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.]

薄暮涼年 2024-12-04 23:36:59

忘记封装、不变性、优化和所有其他大词。如果你想编写好的java代码,我建议你只使用getter,因为它是java友好的,最重要的是它可以节省大量的时间来搜索原因。

例如,您在编写代码时可能不会期望使用流,但后来您发现

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..
¯\_(ツ)_/¯

这个列表可长可短,或者可能对您的用例没有任何意义。但你必须花时间说服自己(或你的代码审查者)为什么你可以或应该反抗 Java 正统观念。

最好只编写 getter/setter 并花时间做一些更有用的事情:比如抱怨 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.

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

梦里兽 2024-12-04 23:36:59

从 Java 16 开始,您可以使用记录。

public record Immu(String foo, String bar) {}

记录的所有属性都自动为最终属性,并且它自动具有 equals(…)toString() 等方法以及构造函数。

属性的 getter 与属性具有相同的名称,在本例中,它们是 foo()bar()

这些方法可以被覆盖,更多信息文档中。

Since Java 16, you can use records.

public record Immu(String foo, String bar) {}

All of a record's attributes are automatically final and it automatically has methods like equals(…) and toString() and the constructor.

The getters of the attributes have the same name as the attributes, in this case, they are foo() and bar().

The methods can be overridden, more information is in the documentation.

寂寞美少年 2024-12-04 23:36:59

目前尚不清楚是否有人会通过 API 使用您的代码。
如果您稍后需要验证输入,您也会错过验证输入的机会。

It is not very clear if someone is going to use your code through an API.
You are also missing an opportunity to validate the input, if you are going to require some later.

虐人心 2024-12-04 23:36:59

对于这样的小工作,使用 public Final 可能没问题,但是它不能作为标准做法

请考虑下面的情况。

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

当然,由于是不可变的,这个对象是通过构造函数初始化的,然后直接访问。我必须告诉你其中的问题吗?很明显!

考虑您的客户编写如下代码 -

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

这可行吗?您的客户端库能够操纵对象的状态,或者更确切地说能够侵入您的运行时表示。这是一个巨大的安全风险,当然像 SONAR 这样的工具可以预先发现它。但只有当你使用 getter-setter 时它才可以管理。

如果您正在使用 getters,您可以很好地编写

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

这可以防止您受到潜在的安全威胁。

查看上面的示例,如果您使用数组,则强烈建议不要使用 public final。在这种情况下,它就不能成为标准。像我这样的人将避免使用无法成为所有数据类型统一标准的代码实践。你呢?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文