我最近从Eclipse切换到IntelliJ,并迅速引入了@Contract
批注,我立即喜欢这些批注背后的概念(“在调用已批注方法的方法中发现问题” )。我的第一个想法是使用它们来定义getter
和utility
方法,而pure
注释参数似乎符合要求:
pure属性适用于不更改其对象状态而仅返回新值的方法。如果未使用其返回值,则除非方法调用引发异常(不将异常视为副作用),否则删除其调用不会影响程序状态或更改语义。
不过,我对上述内容的真正含义感到困惑。
提到的对象是否引用通过方法参数或类字段传递的对象?再者,什么可以作为更改,更改对象引用,内容或可能同时包含两者?
在纯方法内调用更改这些对象的状态的方法是否违反合同?类似于调用构造函数的构建器方法。
什么时候客观地认为将方法标记为纯净的最佳实践?我知道这似乎是一个主观的问题,但必须建立有关何时执行此操作的社区准则(可以参考)。
答案 0 :(得分:1)
一个例子可能正在澄清。考虑以下代码:
class X {
public void main(String[] ss) {
ss[0].toUpperCase(java.util.Locale.ENGLISH);
}
}
String.toUpperCase()
调用不会更改调用的对象ss[0]
,也不会更改提供的参数java.util.Locale.ENGLISH
。它返回一个新对象,但是该对象未在代码中使用。该调用不会更改程序中正在使用的任何对象的状态。因此,删除该调用不会更改程序的行为。 (严格来说,它的运行速度会稍快一些,但这不被视为语义上的相关更改。)
请注意,使用空数组调用main
方法时,它将引发异常。删除呼叫后,将不会发生。但是出于@Contract
注释的目的,异常不被视为副作用。可以将异常视为返回值的一种。
因此String.toUpperCase()
方法可以注释为纯方法。在IntelliJ IDEA中,许多地方都专门处理了纯方法。例如,如果未使用的变量是通过纯方法初始化的,则快速修复程序“知道”可以在删除变量时安全地删除初始化程序。
字母通常是纯净的,void
方法通常不是纯净的。但是也有例外,例如junit.framework.Assert.fail()
是void
但pure
,因为它没有副作用。据我所知,尚无官方指南。
答案 1 :(得分:1)
提到的对象是否引用通过方法参数或类字段传递的对象?
调用该方法的实例的传递对象和字段。
此外,什么才是更改,更改对象引用,内容或可能两者兼而有之?
两者。任何更改均符合资格。
在纯方法内调用更改这些对象的状态的方法是否违反合同?
是的。杂质具有传染性:从另一种方法调用非纯方法也会使后者成为非纯方法。
关键部分是这样:
如果未使用其返回值,则除非方法调用引发异常(不将异常视为副作用),否则删除其调用不会影响程序状态或更改语义。
除了例外之外,另一种查看方式是:只要内联该方法不会改变程序的语义,该方法就是纯净的。