如何确定Java方法是否修改作为参数传递的对象

时间:2015-08-23 07:48:04

标签: java immutability

我来自C ++背景,目前我正在学习Java。当我尝试使用某些第三方库时出现了一个问题。如何确定对作为参数的对象引用的方法的调用是否修改了对象? 在C ++中,由于使用了const关键字,因此很清楚。如果方法签名是:

void foo(Boo& boo);

我知道引用的对象可能会被修改,而方法签名是:

void foo(const Boo& boo);

编译器保证不会修改引用的对象。

我没有在Java中看到类似的东西,因为只有引用本身可以被声明为final,而不是引用的对象,并且最终的参数在第一个地方没有多大意义,因为它无论如何都是通过值传递的。因此,当我看到一个方法,如:

void foo(Boo boo) {...}

如何确定boo引用的对象是否在函数体内修改(可能使用注释)?如果没有办法知道,是否有一些广泛使用的惯例或一些最佳做法,以避免混淆和错误?

7 个答案:

答案 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语言中没有内置副作用分析。

您可以通过手动检查进行副作用分析,但有几种工具可以自动化该过程。

您可以使用推理工具(123)来检测代码是否会对参数产生副作用。

您还可以在代码中编写纯度或副作用注释,然后使用检查/验证工具(12)来确保您的代码符合您编写的注释。

所有上述链接工具都有局限性,但您可能会发现它们很有用。如果您了解其他工具,请在评论中提及。

答案 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表示不会修改传递的对象。