我来自C ++背景,目前我正在学习Java。当我尝试使用某些第三方库时出现了一个问题。如何确定对作为参数的对象引用的方法的调用是否修改了对象? 在C ++中,由于使用了const关键字,因此很清楚。如果方法签名是:
void foo(Boo& boo);
我知道引用的对象可能会被修改,而方法签名是:
void foo(const Boo& boo);
编译器保证不会修改引用的对象。
我没有在Java中看到类似的东西,因为只有引用本身可以被声明为final,而不是引用的对象,并且最终的参数在第一个地方没有多大意义,因为它无论如何都是通过值传递的。因此,当我看到一个方法,如:
void foo(Boo boo) {...}
如何确定boo引用的对象是否在函数体内修改(可能使用注释)?如果没有办法知道,是否有一些广泛使用的惯例或一些最佳做法,以避免混淆和错误?
答案 0 :(得分:25)
如何确定boo引用的对象是否在函数体内修改(可能使用注释)?
唯一的方法是不幸地阅读代码。
如果没有办法知道,是否有一些广泛使用的惯例或一些最佳做法,以避免混淆和错误?
常见的约定是在需要时使用包装器传递无法修改的对象。这样可以确保类无法修改对象。
List<String> readOnly = Collections.unmodifiableList(list);
如果对象是Cloneable,您也可以使用clone()
,但另一种常用方法是使用副本。
List<String> readOnly = new ArrayList<>(list);
如果您关心此类行为,单元测试可以显示方法是否修改了对象。如果您已经进行了单元测试,通常需要额外一两行来检查。
答案 1 :(得分:13)
遗憾的是,这种语言并没有内置。一个好的防御性做法是将您传递的数据对象定义为不可变的(即,没有任何允许修改其状态的公共方法)。如果您真的担心这一点,您可以在将对象传递给您不信任的方法之前复制/克隆该对象,但这通常是一种多余的预防措施。
答案 2 :(得分:8)
注意:此答案是
的更详细版本You can also write purity or side-effect annotations in your code - mernst
在编译时可以通过注释检查的各种事物中存在Checker Framework IJG Immutablity checker。此检查器允许您使用@Immutable
或@ReadOnly
注释对象引用。
问题是你经常需要自己annotate the library。为了简化您的任务,Checker Framework可以automatically infer部分注释;你自己还是要做很多事。
答案 3 :(得分:6)
Java语言中没有内置副作用分析。
您可以通过手动检查进行副作用分析,但有几种工具可以自动化该过程。
您可以使用推理工具(1,2,3)来检测代码是否会对参数产生副作用。
您还可以在代码中编写纯度或副作用注释,然后使用检查/验证工具(1,2)来确保您的代码符合您编写的注释。
所有上述链接工具都有局限性,但您可能会发现它们很有用。如果您了解其他工具,请在评论中提及。
答案 4 :(得分:3)
如何确定boo引用的对象是否在内部修改 函数的主体(可能使用注释)?
我必须同意其他答案,即没有直接的方法来确定该方法是否会修改您的对象,是的,以确保该方法无法修改您的Object
您必须这样做是来自您的侧。
如果没有办法知道,是否有一些广泛使用的惯例或 一些避免混淆和错误的最佳做法?
此处 方法名称 进入场景。继续使用方法的命名约定,我们必须查看一些方法声明,这些声明明确说服您Object
根本不会更改。
例如,您知道Arrays.copyOf
不会更改您的实际数组,System.out.println(boo)
不会更改您的boo
方法名称是真正的武器,以便为方法用户提供尽可能多的信息。(是的!它总是不可能,但是遵循这个很好的做法。)
让我们考虑一下,printBoo
只会打印,copyBoo
只会复制,clearBoo
会重置所有属性,checkAndCreateNewBoo
会检查您的{{1} }} boo
并根据需要创建新的。
因此,最终如果我们能够以正确的方式使用它们,可以确保调用者在调用该方法后Object
将保持不变的事实。
答案 5 :(得分:2)
正如大家所说,更喜欢使用不可变对象,也避免使用 void方法
此类方法的可用目的
...
bestPictures.clearPrefetchCache();
bestPictures.initialize();
$('#remote .typeahead').typeahead(null, {
name: 'best-pictures',
display: 'value',
source: bestPictures
});
是更改对象本身的状态或更改作为参数传递的对象
void foo(Boo boo) {...}
答案 6 :(得分:-3)
有一种方法,方法开发人员应将参数标记为final,如果它不打算修改参数。
public void test(final Object param)
然而很少有人遵循这一点,所以很难知道。然而,优秀的程序员遵循这条规则,尤其是编写api。如果要编写方法并公开它。 make param final表示不会修改传递的对象。