我遇到非虚方法缺少返回语句并且代码仍然编译的情况。 我知道while循环之后的语句是无法访问(死代码),永远不会被执行。但是为什么编译器甚至不警告返回什么呢?或者为什么一种语言允许我们使用具有无限循环且不返回任何内容的非void方法?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
如果我在while循环中添加break语句(甚至是条件语句),编译器会抱怨臭名昭着的错误:Eclipse中的Method does not return a value
和Visual Studio中的Not all code paths return a value
。
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
Java和C#都是如此。
答案 0 :(得分:240)
为什么语言允许我们使用具有无限循环且不返回任何内容的非void方法?
非void方法的规则是返回的每个代码路径都必须返回一个值,并且该规则在您的程序中得到满足:返回的零代码路径中的零返回值。规则不是“每个非void方法都必须有一个返回的代码路径”。
这使您可以编写如下的存根方法:
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
这是一种非虚方法。为了满足接口, 是一个非void方法。但是让这个实现变得非法似乎很愚蠢,因为它不会返回任何东西。
由于goto
,您的方法有一个无法访问的终点(请记住,while(true)
只是一种更愉快的方式来编写goto
)而不是throw
(这是goto
)的另一种形式是不相关的。
为什么编译器甚至不警告返回什么?
因为编译器没有很好的证据证明代码是错误的。有人写了while(true)
,这样做的人很可能知道他们在做什么。
我在哪里可以阅读有关C#中可达性分析的更多信息?
请参阅我关于此主题的文章:
ATBG: de facto and de jure reachability
您也可以考虑阅读C#规范。
答案 1 :(得分:38)
Java编译器非常智能,可以找到无法访问的代码(while
循环后的代码)
并且由于其无法访问,在其中添加return
语句(while
结束后)没有意义
与条件if
public int get() {
if(someBoolean) {
return 10;
}
else {
return 5;
}
// there is no need of say, return 11 here;
}
由于布尔条件someBoolean
只能评估为true
或false
,因此无需明确提供return
if-else
,因为该代码无法访问,而且Java不会抱怨它。
答案 2 :(得分:17)
编译器知道while
循环永远不会停止执行,因此该方法永远不会完成,因此不需要return
语句。
答案 3 :(得分:13)
鉴于你的循环正在一个常量上执行 - 编译器知道它是一个无限循环 - 意味着该方法永远不会返回。
如果使用变量 - 编译器将强制执行规则:
这不会编译:
// Define other methods and classes here
public int doNotReturnAnything() {
var x = true;
while(x == true) {
//do something
}
//no return statement - won't compile
}
答案 4 :(得分:11)
Java规范定义了一个名为Unreachable statements
的概念。您不允许在代码中包含无法访问的语句(这是编译时错误)。你甚至不允许在while之后有一个return语句(true); Java中的声明。 while(true);
语句根据定义使以下语句无法访问,因此您不需要return
语句。
请注意,虽然Halting problem在通用情况下是不可判定的,但是无法到达语句的定义比仅停止更严格。它正在决定一个程序肯定不会停止的非常具体的情况。理论上,编译器无法检测所有无限循环和无法访问的语句,但它必须检测规范中定义的特定情况(例如,while(true)
情况)
答案 5 :(得分:7)
编译器非常聪明,可以发现你的while
循环是无限的。
所以编译器无法为你考虑。它无法猜测为什么您编写了该代码。同样代表方法的返回值。如果你不对方法的返回值做任何事情,Java就不会抱怨。
所以,回答你的问题:
编译器会分析您的代码,并在发现没有执行路径导致函数结束时,它就完成了OK。
无限循环可能有合理的原因。例如,许多应用程序使用无限主循环。另一个例子是可以无限期等待请求的Web服务器。
答案 6 :(得分:7)
在类型理论中,有一种称为底部类型的东西,它是所有其他类型(!)的子类,用于表示其他事物的非终止。 (例外可以算作一种非终止 - 你不会通过正常路径终止。)
所以从理论的角度来看,这些非终止的语句可以被认为是返回Bottom类型的东西,这是int的子类型,所以你从类型的角度来看(从某种程度上)得到你的返回值。并且完全没关系,没有任何意义,一个类型可以是其他所有的子类,包括int,因为你实际上从未返回过。
在任何情况下,通过显式类型理论,编译器(编译器编写者)都认识到在非终止语句之后要求返回值是愚蠢的:没有可能需要该值的情况。 (让你的编译器在知道某些东西不会终止但是看起来你想让它返回一些东西时警告你可能会很高兴。但是对于样式检查器而言,这样做更好,因为你可能需要类型签名,出于某种其他原因(例如,子类化),但你真的想要非终止。)
答案 7 :(得分:6)
在没有返回适当值的情况下,函数无法达到目的。因此,编译器没有什么可抱怨的。
答案 8 :(得分:5)
Visual Studio有智能引擎来检测你是否输入了一个返回类型,然后它应该在函数/方法中有一个return语句。
与在PHP中一样如果您没有返回任何内容,则返回类型为true。如果没有返回,编译器将获得1。
截至此
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
编译器知道虽然语句本身具有infinte性质,所以不要考虑它。如果你用while。
的表达式写一个条件,php编译器会自动变为true但不是在VS的情况下,它会在堆栈中返回错误。
答案 9 :(得分:4)
你的while循环将永远运行,因此不会出现在外面;它将继续执行。因此,while {}的外部部分是无法访问的,并且没有写入返回的点。编译器足够智能,可以确定哪些部分可以访问,哪些部分不可访问。
示例:
public int xyz(){
boolean x=true;
while(x==true){
// do something
}
// no return statement
}
上面的代码不会编译,因为可能存在变量x的值在while循环体内被修改的情况。所以这使得while循环的外部部分可以访问!因此编译器将抛出错误'找不到返回语句'。
编译器不够智能(或者说是懒惰;))以确定是否会修改x的值。希望这能清除一切。
答案 10 :(得分:4)
“为什么编译器甚至不警告返回某些内容?或者为什么语言允许我们使用无效循环并且不返回任何内容的非void方法?”。
此代码在所有其他语言中也有效(可能除了Haskell!)。因为第一个假设是我们“有意”地编写了一些代码。
在某些情况下,如果您要将此代码用作线程,则此代码可以完全有效;或者如果它返回Task<int>
,您可以根据返回的int值进行一些错误检查 - 不应该返回。
答案 11 :(得分:3)
我可能错了,但有些调试器允许修改变量。这里虽然x没有被代码修改而且它将被JIT优化,但是可能会将x修改为false并且方法应该返回一些内容(如果C#调试器允许这样的话)。