让我们假设我们有以下课程:
public class MyClass{
List<String> list;
void method() {
}
}
这个类的每个对象都有一个字符串列表,但如果我们想设置基数怎么办?例如,我想强制此类在该列表中包含至少 2个字符串。
是否有一般模式来表示字段的基数?
答案 0 :(得分:3)
您只需要通过某种方式确保list
中至少有2个元素。没有标准或简单的方法。
这被称为该类的不变。作为编写课程的人,您有责任确保始终保留此不变量。特别是:
list
在构造函数完成时至少包含两个元素。当构造函数完成时,该实例应该是“有效的”(在其记录的不变量为真的意义上)。您需要确保类中的所有代码在操作列表时遵循不变量 - 您可以暂时使其无效,但您必须确保无法在无效中观察您的实例状态。
在单线程的情况下,这只是意味着一旦public方法返回,invariant必须为true(注意这包括是否发生异常);但如果您的代码设计为以多线程方式使用,则还必须确保没有线程可以在不变量为false的状态下看到您的实例(例如,您可能需要synchronized
块,或确保更新是原子的。
您需要确保您的类的子类不能使不变量为false;或明确记录编写子类的人员有责任确保不变量保持为真。
Effective Java 2nd Ed 中有一个很棒的项目:“继承的设计和文档,否则禁止它。”
您需要确保班级以外的任何内容都无法访问列表的引用。例如,您的代码使列表对同一个包中的其他类可见 - 这些类中的任何一个都可以调用theInstance.list.clear()
,并使您的不变量无效。
很难完全阻止这种情况 - 例如,恶意行为者可能有可能使用反射使不变量无效。你可以阻止这种情况,但这是一个权衡识别和阻止这些方法的努力与不变量的实际成本变为假的问题(这在很大程度上取决于如何使用这个类)。
到目前为止,强制执行不变量的最简单方法是在不可变类上。如果无法更改实例的可观察状态,则无法使不变量无效。然后,您需要担心的是a)确保该类是不可变的; b)确保构造函数返回后不变量为真。上面所有其他要点就会消失。
答案 1 :(得分:1)
是否有一般模式来表示字段的基数?
显然,您可以使用整数字段来表示基数,无论是在对象本身还是在元级别。但如果你不能强制执行它们,那就没什么用了。
没有一般的模式。 @Andy Turner的回答提供了关于执行基数的替代方案的一个很好的总结。我只想补充几点:
尝试通过静态类型强制执行基数约束不太可行。 Java类型系统不够丰富,无法以愉快的方式执行此操作 1 。
构建具有最小基数的字段的对象可能会非常棘手,尤其是在存在涉及这些字段的潜在循环的情况下。
处理构造的一种方法是将对象的生命周期分为“构造”阶段和“完成”阶段。在施工阶段,放松约束,以允许分阶段进行施工。在某些时候,“完成”开关被“翻转”。在那一点1)检查基数约束,2)改变变异操作的行为以防止违反基数的变化。
这可以使用公共构造函数和公共方法来实现“翻转开关”。或者,您可以使用builder pattern实现此目的; e.g。
build()
方法中翻转开关(需要一个)。另一种方法是允许字段低于基数,但只允许在项目处于该状态时将项目添加到字段中。换句话说,这是没有显式切换的“翻转开关”方法。缺点是客户需要测试基数约束是否有效;即约束力很弱。
1 - 理论上,你可以实现一个ListOfTwoOrMore
接口等,但这会带来一系列新问题。
答案 2 :(得分:0)
这样做的一种方法是为字段使用不同的类型。现在它具有类型List<String>
,它是一个可以包含0个或更多元素的集合。您可以将其更改为表示包含2个或更多元素的列表的类型。
答案 3 :(得分:0)
您可以在构造函数中使用var-args。
public MyClass(String s1, String s2, String... rest){
}