Java 不可变类型:公共最终字段与getter
我需要一个小容器类来存储一些应该是不可变的字符串。由于字符串本身是一种不可变的类型,我想到了类似的东西:Java 不可变类型:公共最终字段与getter,java,immutability,Java,Immutability,我需要一个小容器类来存储一些应该是不可变的字符串。由于字符串本身是一种不可变的类型,我想到了类似的东西: 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; } } 许多人似乎完全反对使用公共字段
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;
}
}
许多人似乎完全反对使用公共字段,而是使用getter。在这种情况下,这只是一个样板,因为字符串本身是不可变的
在这一点上,我可能缺少其他想法?问题在于统一访问原则。您以后可能需要修改
foo
,以便通过一种方法而不是固定的方法获得它,如果您公开了字段而不是getter,则需要破坏API。我会做您认为最简单、最清晰的事情。如果您有一个数据值类,该类仅由数量有限的类使用。esp是一个包本地类。然后我将避免使用getter/setter,并使用包本地或公共字段
如果您有一个希望其他模块/开发人员使用的类,那么从长远来看,遵循getter/setter模型可能是更安全的方法。不太清楚是否有人将通过API使用您的代码。
如果您以后需要验证输入,您也将错过验证输入的机会。此答案将被排除: 为什么不呢
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 ; }
}
}
我在主项目中使用公共final字段(anti?)模式来处理类,这些类基本上是一个具有构造函数的不可变数据结构,如果需要,还可以使用equals()、hashCode()、toString()等绝对基础。(我避免使用“struct”一词,因为对它有各种不同的语言解释。) 我不会将这种方法带到其他人的代码库(工作、公共项目等),因为它可能与其他代码不一致,并且优先考虑的原则是在罗马或最不意外 也就是说,关于Daniel C.Sobral和aioobe的答案,我的态度是,如果类设计因为不可预见的发展而成为问题,那么IDE中的字段私有化和添加访问器只需30秒,修复损坏的引用不超过5到10分钟,除非有数百个。任何失败的结果都会得到它本来应该进行的单元测试。:-)
[编辑:高效Java非常坚决地反对这一观点,同时指出它对不可变字段的“危害较小”。我发现这条线程希望得到一些实际的论点,但我在这里看到的答案对我没有多大帮助。经过更多的研究和思考,我认为必须考虑以下几点:
对于不可变类型来说看起来最干净public final
- 可变类型可以由访问器更改,即使这不是预期的-在并发环境中,这可能会导致很多麻烦
- 构造函数中不能有参数。如果您需要工厂方法(例如LMAX干扰器),这一点非常重要。以类似的方式,通过反射实例化对象变得更加复杂
- 能手和二传手可能有副作用。使用
可以清楚地告诉程序员,没有隐藏的魔法发生,对象本身就是哑的:)public final
- 不能将包装器或派生类实例返回给访问器。同样,当字段被赋值时,您应该知道这一点。在我看来,容器类不应该关心返回给谁
如果您处于开发中期,没有任何指导方针阻止您,并且项目是孤立的,或者您可以控制所有涉及的项目,我建议对不可变类型使用
public final
。如果您决定以后需要getter,Eclipse提供了Refactor
->封装字段…
,它会自动创建这些字段并调整对该字段的所有引用。忘记封装、不变性、优化和所有其他大字。如果您试图编写好的java代码,我建议您只使用getter,因为它对java友好,而且最重要的是,它可以节省大量时间在Google上搜索why
例如,您可能不希望在编写代码时使用流,但后来您发现
listOfImmus.stream().map(immu->imm.foo).collect(Collectors.toSet());//带场
listOfImmus.stream().map(Immu::getFoo.collect(Collectors.toSet());//带吸气剂
供应商s=()->immu.foo;//带场
供应商s=immu::foo;//带吸气剂
//最终字段即使不是不可能的,也很难模拟。
Mockito.when(immuMock.getFoo())。然后返回(“what ever”);
//有一天,您的代码被用于需要setter-getter的java bean中。。
¯\_(ツ)_/¯
这个列表可以是长的,也可以是短的,或者可能对您的用例没有任何意义。但是您必须花时间说服自己(或您的代码审阅者)为什么您可以或应该反抗java正统
最好只编写getter/setter,然后花时间做一些更有用的事情:比如抱怨java,使用public-final可能适合这么小的工作,但它不能作为一种标准实践
考虑下面的情况
Public class Portfolio {
public final String[] stocks;
}
当然,由于是不可变的,这个对象被初始化为vis构造函数,然后直接访问。我必须告诉你其中的问题吗?很明显
考虑您的客户编写如下代码-
Portfolio portfolio = PortfolioManager.get(“Anand”);
Portfolio.stocks[0] = “FB”;
portfolio.calculate();
这可行吗?您的客户端库能够操纵对象的状态,或者更确切地说,能够在运行时表示中进行攻击。这是一个巨大的安全风险,当然像SONAR这样的工具会提前发现它。但只有在您使用getter setter时,它才是可管理的
如果您使用的是getter,那么您可以很好地编写
Public class Portfolio {
private final String[] stocks;
public String[] getStocks() {
return Arrays.coptOf(this.stocks);
}
}
这可以防止您受到潜在的安全威胁
看看上面的例子,如果您使用的是数组,那么强烈建议不要使用公共final