如何确定Java方法是否修改作为参数传递的对象 我来自C++背景,目前正在学习java。当我尝试使用一些第三方库时,出现了一个问题。如何确定对以对象引用为参数的方法的调用是否会修改对象? 在C++中,这要通过使用const关键字来实现。如果方法签名为: void foo(Boo& boo); void foo(const Boo& boo);

如何确定Java方法是否修改作为参数传递的对象 我来自C++背景,目前正在学习java。当我尝试使用一些第三方库时,出现了一个问题。如何确定对以对象引用为参数的方法的调用是否会修改对象? 在C++中,这要通过使用const关键字来实现。如果方法签名为: void foo(Boo& boo); void foo(const Boo& boo);,java,immutability,Java,Immutability,我知道引用的对象可能会被修改,而如果方法签名是: void foo(Boo& boo); void foo(const Boo& boo); 编译器保证引用的对象不会被修改 我在Java中没有看到类似的东西,因为只有引用本身可以声明为final,而不是被引用的对象,final参数在一开始就没有多大意义,因为它是通过值传递的。因此,当我看到以下方法时: void foo(Boo boo) {...} 如何确定boo引用的对象是否在函数体内部修改(可能使用注释)?如果没有办法知

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

void foo(Boo& boo);
void foo(const Boo& boo);
编译器保证引用的对象不会被修改

我在Java中没有看到类似的东西,因为只有引用本身可以声明为final,而不是被引用的对象,final参数在一开始就没有多大意义,因为它是通过值传递的。因此,当我看到以下方法时:

void foo(Boo boo) {...}

如何确定boo引用的对象是否在函数体内部修改(可能使用注释)?如果没有办法知道,是否有一些广泛使用的约定或最佳实践来避免混淆和bug?

不幸的是,该语言中没有内置这样的功能。一个好的防御实践是将传递的数据对象定义为不可变的(即,没有任何允许修改其状态的公共方法)。如果您确实关心这个问题,可以在将对象传递给不信任的方法之前复制/克隆对象,但这通常是多余的预防措施

如何确定boo引用的对象是否在函数体内部修改(可能使用注释)

唯一的方法是读取代码

如果没有办法知道,是否有一些广泛使用的约定或最佳实践来避免混淆和bug

常见的约定是传递一个不能修改的对象,如果需要,使用包装器。这将确保类无法修改对象

List<String> readOnly = Collections.unmodifiableList(list);

如果您关心这种行为,单元测试可以显示方法是否修改对象。如果您已经有了单元测试,通常需要额外检查一两行。

有一种方法,方法开发人员应该将参数标记为final,如果它不打算修改参数

     public void test(final Object param)

然而,很少有人关注这一点,因此很难知道。然而,优秀的程序员遵循这条规则,尤其是编写api。如果您想编写方法并公开它。将param设为final以指示传递的对象不会被修改。

Java语言中没有内置副作用分析

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

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

     public void test(final Object param)
您还可以在代码中编写纯注释或副作用注释,然后使用检查/验证工具(,)确保代码符合您编写的注释

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

注意:此答案是

-

在编译时通过注释可以检查的各种事情中,有一个是。此检查器允许您使用或注释对象引用

问题是,你常常不得不对自己说。为了简化任务,检查器框架可以作为注释的一部分;你仍然需要自己做很多事情

如何确定boo引用的对象是否在内部修改 函数体(可能使用注释)

我必须同意其他答案,即没有直接的方法来确定该方法是否会修改您的对象,是的,确保该方法不能修改您的
对象
,您必须从您的角度来做

如果没有办法知道,是否有一些广泛使用的惯例或规则 一些避免混淆和bug的最佳实践

这里,方法名出现在场景中。继续讨论方法的命名约定,我们必须看看一些方法声明,它们清楚地说服您,您的
对象
将不会发生任何更改

例如,您知道
array.copyOf
不会更改您的实际数组,
System.out.println(boo)
不会更改您的
boo

方法名称是向方法用户提供尽可能多的信息的真正武器。(是的!这总是不可能的,但遵循它是一个很好的实践。)

让我们考虑在你的例子中说“代码> PrtPub<代码>将只打印,<代码> CopyBo< <代码>将只复制,<代码> CurrBoo> /代码>将重置所有属性,<代码>检查和创建WEBO< <代码>将检查您的<代码> BOO <代码>对象< /代码>,并在需要时创建新的.< /P>


因此,如果我们能够以适当的方式使用它们,调用方最终可以确信,
对象
在调用该方法后将保持不变。

正如大家所说,更喜欢使用不可变对象,同时也避免使用无效方法

这样的方法的可用目的

void foo(Boo boo) {...}
是更改对象本身的状态还是更改作为参数传递的对象

void completOrder(Order order) { ... }
//or
void parserTokenEnded(String str) { ... }

阅读文档。(您可能认为这是java相对于C++的弱点,但我们并不真正错过它,正如您在C++中工作时忽略了检查异常)。最好的做法是避免可能发生的变异。这适用于所有情况,而不仅仅是方法的参数。@user2357112“在C++中工作时,如果遗漏了检查过的异常,那么使用异常控制程序流是错误的,异常(正如其名称所示)适用于异常情况。拥有一种语言功能,其唯一目的是能够使用异常从错误中恢复,因为通信通道只是“除外”的另一种情况