可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I often find when debugging a program it is convenient, (although arguably bad practice) to insert a return statement inside a block of code. I might try something like this in Java ....
class Test {
public static void main(String args[]) {
System.out.println(\"hello world\");
return;
System.out.println(\"i think this line might cause a problem\");
}
}
of course, this would yield the compiler error.
Test.java:7: unreachable statement
I could understand why a warning might be justified as having unused code is bad practice. But I don\'t understand why this needs to generate an error.
Is this just Java trying to be a Nanny, or is there a good reason to make this a compiler error?
回答1:
Because unreachable code is meaningless to the compiler. Whilst making code meaningful to people is both paramount and harder than making it meaningful to a compiler, the compiler is the essential consumer of code. The designers of Java take the viewpoint that code that is not meaningful to the compiler is an error. Their stance is that if you have some unreachable code, you have made a mistake that needs to be fixed.
There is a similar question here: Unreachable code: error or warning?, in which the author says \"Personally I strongly feel it should be an error: if the programmer writes a piece of code, it should always be with the intention of actually running it in some scenario.\" Obviously the language designers of Java agree.
Whether unreachable code should prevent compilation is a question on which there will never be consensus. But this is why the Java designers did it.
A number of people in comments point out that there are many classes of unreachable code Java doesn\'t prevent compiling. If I understand the consequences of Gödel correctly, no compiler can possibly catch all classes of unreachable code.
Unit tests cannot catch every single bug. We don\'t use this as an argument against their value. Likewise a compiler can\'t catch all problematic code, but it is still valuable for it to prevent compilation of bad code when it can.
The Java language designers consider unreachable code an error. So preventing it compiling when possible is reasonable.
(Before you downvote: the question is not whether or not Java should have an unreachable statement compiler error. The question is why Java has an unreachable statement compiler error. Don\'t downvote me just because you think Java made the wrong design decision.)
回答2:
There is no definitive reason why unreachable statements must be not be allowed; other languages allow them without problems. For your specific need, this is the usual trick:
if (true) return;
It looks nonsensical, anyone who reads the code will guess that it must have been done deliberately, not a careless mistake of leaving the rest of statements unreachable.
Java has a little bit support for \"conditional compilation\"
http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21
if (false) { x=3; }
does not result in a compile-time
error. An optimizing compiler may
realize that the statement x=3; will
never be executed and may choose to
omit the code for that statement from
the generated class file, but the
statement x=3; is not regarded as
\"unreachable\" in the technical sense
specified here.
The rationale for this differing
treatment is to allow programmers to
define \"flag variables\" such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible
to change the value of DEBUG from
false to true or from true to false
and then compile the code correctly
with no other changes to the program
text.
回答3:
It is Nanny.
I feel .Net got this one right - it raises a warning for unreachable code, but not an error. It is good to be warned about it, but I see no reason to prevent compilation (especially during debugging sessions where it is nice to throw a return in to bypass some code).
回答4:
I only just noticed this question, and wanted to add my $.02 to this.
In case of Java, this is not actually an option. The \"unreachable code\" error doesn\'t come from the fact that JVM developers thought to protect developers from anything, or be extra vigilant, but from the requirements of the JVM specification.
Both Java compiler, and JVM, use what is called \"stack maps\" - a definite information about all of the items on the stack, as allocated for the current method. The type of each and every slot of the stack must be known, so that a JVM instruction doesn\'t mistreat item of one type for another type. This is mostly important for preventing having a numeric value ever being used as a pointer. It\'s possible, using Java assembly, to try to push/store a number, but then pop/load an object reference. However, JVM will reject this code during class validation,- that is when stack maps are being created and tested for consistency.
To verify the stack maps, the VM has to walk through all the code paths that exist in a method, and make sure that no matter which code path will ever be executed, the stack data for every instruction agrees with what any previous code has pushed/stored in the stack. So, in simple case of:
Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);
at line 3, JVM will check that both branches of \'if\' have only stored into a (which is just local var#0) something that is compatible with Object (since that\'s how code from line 3 and on will treat local var#0).
When compiler gets to an unreachable code, it doesn\'t quite know what state the stack might be at that point, so it can\'t verify its state. It can\'t quite compile the code anymore at that point, as it can\'t keep track of local variables either, so instead of leaving this ambiguity in the class file, it produces a fatal error.
Of course a simple condition like if (1<2)
will fool it, but it\'s not really fooling - it\'s giving it a potential branch that can lead to the code, and at least both the compiler and the VM can determine, how the stack items can be used from there on.
P.S. I don\'t know what .NET does in this case, but I believe it will fail compilation as well. This normally will not be a problem for any machine code compilers (C, C++, Obj-C, etc.)
回答5:
One of the goals of compilers is to rule out classes of errors. Some unreachable code is there by accident, it\'s nice that javac rules out that class of error at compile time.
For every rule that catches erroneous code, someone will want the compiler to accept it because they know what they\'re doing. That\'s the penalty of compiler checking, and getting the balance right is one of the tricker points of language design. Even with the strictest checking there\'s still an infinite number of programs that can be written, so things can\'t be that bad.
回答6:
While I think this compiler error is a good thing, there is a way you can work around it.
Use a condition you know will be true:
public void myMethod(){
someCodeHere();
if(1 < 2) return; // compiler isn\'t smart enough to complain about this
moreCodeHere();
}
The compiler is not smart enough to complain about that.
回答7:
It is certainly a good thing to complain the more stringent the compiler is the better, as far as it allows you to do what you need.
Usually the small price to pay is to comment the code out, the gain is that when you compile your code works. A general example is Haskell about which people screams until they realize that their test/debugging is main test only and short one. I personally in Java do almost no debugging while being ( in fact on purpose) not attentive.
回答8:
If the reason for allowing if (aBooleanVariable) return; someMoreCode;
is to allow flags, then the fact that if (true) return; someMoreCode;
does not generate a compile time error seems like inconsistency in the policy of generating CodeNotReachable exception, since the compiler \'knows\' that true
is not a flag (not a variable).
Two other ways which might be interesting, but don\'t apply to switching off part of a method\'s code as well as if (true) return
:
Now, instead of saying if (true) return;
you might want to say assert false
and add -ea OR -ea package OR -ea className
to the jvm arguments. The good point is that this allows for some granularity and requires adding an extra parameter to the jvm invocation so there is no need of setting a DEBUG flag in the code, but by added argument at runtime, which is useful when the target is not the developer machine and recompiling & transferring bytecode takes time.
There is also the System.exit(0)
way, but this might be an overkill, if you put it in Java in a JSP then it will terminate the server.
Apart from that Java is by-design a \'nanny\' language, I would rather use something native like C/C++ for more control.