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

时间:2011-08-03 14:05:50

标签: java immutability

我需要一个小的Container-Class来存储一些应该是不可变的字符串。由于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。恕我直言,在这种情况下,这只是样板文件,因为String本身是不可变的。

我可能在这个问题上缺少其他想法吗?

8 个答案:

答案 0 :(得分:52)

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

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

答案 1 :(得分:24)

问题在于统一访问原则。您以后可能需要修改foo,以便通过方法获取而不是修复,如果您公开了字段而不是getter,则需要中断API。

答案 2 :(得分:14)

这个答案被废除了:

为什么不

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

答案 3 :(得分:6)

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

我不会将此方法带给其他人的代码库(工作,公共项目等),因为它可能与其他代码不一致,并且像Of In Rome或Least Surprise这样的原则优先考虑。

那就是说,关于Daniel C. Sobral和aioobe的答案,我的态度是,如果由于不可预见的发展而使课堂设计成为一个问题,那就是30的工作。在IDE中私有化字段并添加访问器的秒数,并且不超过5或10分钟来修复损坏的引用,除非有数百个。任何因此而失败的东西都会得到它应该首先进行的单元测试。: - )

[编辑:有效的Java非常坚决反对这个想法,同时注意到它的危害较小"在不可变的字段上。]

答案 4 :(得分:5)

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

  • public final对于不可变类型看起来最干净。
  • 即使不是这样,访问者也可以更改可变类型 - 在并发环境中,这可能会导致很多麻烦。
  • 没有无参数构造函数。如果您需要工厂方法(例如LMAX Disruptor),这是重要的。以类似的方式通过反射实例化对象变得更加复杂。
  • 吸气剂和制定者可能有副作用。使用public final清楚地告诉程序员没有隐藏的魔法发生,并且该对象本身就是愚蠢的:)
  • 您无法将包装器或派生类实例返回给访问者。然后,这是你应该知道什么时候字段被赋值。在我看来,容器类不应该关注什么归还给谁。

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

答案 5 :(得分:1)

忘记封装,不变性,优化和所有其他重要词汇。如果您尝试编写良好的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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

对于这样的小职位,使用公开决赛可能会很好,但是不能将其作为标准做法

请考虑以下情况。

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

当然,该对象是不可变的,可以通过构造方法进行初始化,然后直接访问。我必须告诉你其中的问题吗?很明显!

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

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

这可行吗?您的客户端库能够操纵对象的状态,或者能够在您的运行时表示中进行破解。这是一个巨大的安全风险,当然像SONAR这样的工具也可以预先捕获它。但是,只有在使用getter-setter时,它才可管理。

如果使用吸气剂,则可以很好地编写

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

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

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