This question already has answers here:
Unreachable code error vs. dead code warning in Java under Eclipse?
(8 answers)
Closed 6 years ago.
I am messing around a little bit with Java and the return statement of a void-method.
In each of the methods, the compiler within my Eclipse IDE (Eclipse Mars 4.5.2, Java 7) has a different output. I've written the warnings/the error as comments over the regarding line:
public static void foo() {
System.out.println("foo() - 1");
boolean b = true;
if (b)
return;
System.out.println("foo() - 2");
}
public static void foo2() {
System.out.println("foo2() - 1");
// compiler warning: "The value of the local variable b is not used"
boolean b;
if (b = true)
return;
System.out.println("foo2() - 2");
}
public static void foo3() {
System.out.println("foo3() - 1");
if (true) {
return;
}
// compiler warning: "Dead code"
System.out.println("foo3() - 2");
}
public static void foo4() {
System.out.println("foo4() - 1");
return;
// compiler error: "Unreachable code"
System.out.println("foo4() - 2");
}
Each method has the same behaviour. The if-statement is true and the return-statement is invoked. The last System.out.println(..) isn't invoked any more. But why does the compiler outputs different things?
Thanks for your help!
Kind regards
P.S.: My Eclipse looks like that:
P.P.S.: if I compile this file via javac Test.java I don't get any warnings, although I have to disable them manually by using -nowarn (javac documentation), but I did not do that:
One of the main task of Compiler is to check valid syntax
Last method has compilation Error because of after retrun statement no other statement possible.
And previuos methods are compiled because the syntax is correct even though there is clear dead code warning.
So try it other way.
Code analysis is sensitive to "Stop Problem" in general. To obtain some information about the code, you usually have to run the code (if code contains infinite loops, the analyser would hang on during analysis). Because of the issue, in foo and foo2 code analyser doesn't predict future code behaviour.
foo4: It is simply Java syntactic error. It is not allowed to write a code after return statement.
foo3: Code is syntactically correct, but as RC mentioned, code analyser integrated with IDE is able to perform simple detection of a branch that will be never fired.
Its simply because in the first 2 there is no garauntee that condition will not change in runtime (as far as the compiler concerns) , while in the last 2 there is no way the condition will ever change in runtime.
Whats weird is that putting a final there didnt help compiler realize the dead code, altough it is gauranteed that b will never change.
It seems that the compiler doesnt make any effort to evaluate your code in any level to find dead code...
Related
This question already has answers here:
Unreachable code: error or warning? [closed]
(12 answers)
Closed 9 years ago.
class For1
{
public static void main(String args[])
{
int a = 0;
for(;;)
{
break;
System.out.println(a); //Line 1
++a;//Line 2
}
}
}
I know that Line 1/Line 2 will never be executed.
But still I don't understand why a compile time error is thrown.
I am getting "unreachable statement" compile error.
Does it mean that compiler checks whether it is able to compile for all branches/lines of code ?
Does it mean that compiler checks whether it is able to compile for all branches/lines of code ?
It means the compiler checks that every statement is reachable.
From section 14.21 of the JLS:
It is a compile-time error if a statement cannot be executed because it is unreachable.
This section is devoted to a precise explanation of the word "reachable." The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer, or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements.
The section then documents how reachability is defined.
In particular, the relevant points in your case are:
Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.
A break, continue, return, or throw statement cannot complete normally.
So your "line 1" statement is preceded by a statement (break;) which cannot complete normally, and therefore it's unreachable.
The compiler is also able to make that conclusion, and assumes you are making a mistake. And yes, the Java compiler does a pretty good amount of "Data-Flow Analysis". The most common related message is the one about variables not initialized. The second most frequent is, I believe, precisely this one, about code not reachable.
Does it mean that compiler checks whether it is able to compile for
all branches/lines of code ?
Yes compiler compiles the whole body of code and make byte code according to your code, it smarter enough to detects unreachable code also dead code. Immediate break in the for-loop makes unreachable other statements.
for(;;){
break;
... // unreachable statement
}
int i=1;
if(i==1)
...
else
... // dead code
Unreachable code is meaningless and redundant. If you have some unreachable code in your program it is a mistake and needs to be fixed. Hence compiler throws an error.
You can refer to similar questions below
Unreachable code: error or warning?
and
Why does Java have an "unreachable statement" compiler error?
The compiler is able to determine that these two statement will never, ever be executed, and helps you write correct code by refusing to compile it, because this has 99.9% chance of being an error rather than a conscious choice to add statements that will never be executed.
The compiler will check if there is more code after certain keywords. Another keyword which will cause a similar message is if you replace break by return.
I was recently removing a block of code from our code base before a release and used an if(false) statement to prevent execution:
if (false) {
ArrayList<String> list = new ArrayList<String>();
...
}
This compiles fine and will prevent execution of the offending block of code (right or wrong, that's not the current argument).
However, kind of by accident, I changed the block above to:
while (false) {
ArrayList<String> list = new ArrayList<String>();
...
}
and received an unreachable statement compilation error.
I appreciate the compilation error and understand the reasons, however, I'm struggling to comprehend the difference between the two blocks and why the former compiles fine but the latter does not when they both have unreachable statements.
In both case the compiler should raise an error, because the code between braces is essentially pointless, but if (false) was kept in Java to simulate C/C++ preprocessor #if 0, quite a common way of disabling parts of code for testing or debugging.
EDIT: for reference, "conditional compiling" is detailed at the end of chapter 14.21 of the Java Language Specification.
"Java uses a simple flow analysis algorithm to find most common cases of unreachable code, and all such unreachable code blocks will be flagged as compile-time errors. That's why your "while (false) { ... }" statement produces an error.
However, Java makes a special exception for "if (false) { ... }", because programmers often use this construct during development to temporarily disable part of the program. That's why the compiler accepts this statement.
If you're interested in the nitty-gritty details, refer to the Java Language Specification's description of unreachable statements # http://docs.oracle.com/javase/specs/#14.21."
Quoted from http://www.coderanch.com/t/266678/java-programmer-SCJP/certification/false-false
This question already has answers here:
Why does Java have an "unreachable statement" compiler error?
(8 answers)
Closed 6 years ago.
The following code gives an unreachable statement compiler error
public static void main(String[] args) {
return;
System.out.println("unreachable");
}
Sometimes for testing purposes a want to prevent a method from being called, so a quick way to do it (instead of commenting it out everywhere it's used) is to return immediately from the method so that the method does nothing. What I then always do to get arround the compiler error is this
public static void main(String[] args) {
if (true) {
return;
}
System.out.println("unreachable");
}
I'm just curious, why is it a compiler error?? Will it break the Java bytecode somehow, is it to protect the programmer or is it something else?
Also (and this to me is more interesting), if compiling java to bytecode does any kind of optimization (or even if it doesn't) then why won't it detect the blatant unreachable code in the second example? What would the compiler pseudo code be for checking if a statement is unreachable?
Unreachable code is meaningless, so the compile-time error is helpful. The reason why it won’t be detected at the second example is, like you expect, for testing / debugging purposes. It’s explained in The Specification:
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.
Reference: http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21
Its because the compiler writer assumed that the human at the controls is dumb, and probably didn't mean to add code that would never be executed - so by throwing an error, it attempts to prevent you from inadvertently creating a code path that cannot be executed - instead forcing you to make a decision about it (even though, as you have proven, you still can work around it).
This error is mainly there to prevent programmer errors (a swap of 2 lines or more). In the second snippet, you make it clear that you don't care about the system.out.println().
Will it break the Java bytecode somehow, is it to protect the programmer or is it something else?
This is not required as far as Java/JVM is concerned. The sole purpose of this compilation error is to avoid silly programmer mistakes. Consider the following JavaScript code:
function f() {
return
{
answer: 42
}
}
This function returns undefined as the JavaScript engine adds semicolon at the end of the line and ignores dead-code (as it thinks). Java compiler is more clever and when it discoveres you are doing something clearly and obviously wrong, it won't let you do this. There is no way on earth you intended to have dead-code. This somehow fits into the Java premise of being a safe language.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21
says:
14.21. Unreachable Statements
It is a compile-time error if a statement cannot be executed because it is unreachable.
Example 1:
In this case you are return before any statement because of it compiler never going to execute that code.
public static void main(String[] args) {
return;
System.out.println("unreachable");
}
In second code I have put the statement above of return and its work now :)
Example 2:
public static void main(String[] args) {
System.out.println("unreachable"); // Put the statement before return
return;
}
The reason behind this is that if you return sometime then code after it never going to execute because you already return the function data and as so it is shown unreachable code.
It's because it's a waste of resources for it to even be there. Also, the compiler designers don't want to assume what they can strip out, but would rather force you to remove either the code that makes it unreachable or the unreachable code itself. They don't know what is supposed to be there. There's a difference between the optimizations where they tweak your code to be a bit more efficient when it's compiled down to machine code and blatantly just removing code "you didn't need."
I am developing a dynamic web project using Tomcat. It is useful to have a global flag that is the only thing I have to change between my development and deployment servers. The biggest use of the flag is with print statements.
public class Debug {
public final static boolean DEVEL = true;
public static void print(String message){
if(DEVEL){
System.out.println(message);
}
}
}
My question is, will java compile out the print statements. i.e. if the devel tag is false, the messages will obviously not print but will they be included in the class files (devel is final). This is a question of efficiency. I've heard that the java compiler is very smart but will it pick up on this.
use the 'final' attribute (as you did), and it will compile out the code:
public static final boolean DEVEL = false;
You can check by grepping the resultant class file for a string that would appear only if the code were compiled.
Take a look into this article:
http://www.javaworld.com/javaworld/jw-03-2000/jw-03-javaperf_4.html
The code you presented is called a "dead code" so it will not be included in the compiled class file if you set DEVEL to false it will not be included in the bytecode.
Also, check the command
javap -c
to see the resulting bytecode of your class.
If you want to have the compiler not compile it out, use this:
public final static boolean DEVEL = Math.random() > -1;
The compiler won't know that this is always true. (of course use < -1 for false)
I find this trick handy when you want to remove code temporarily without having the compiler complain abut dead code, eg:
void myMethod() {
// temporarily exit early
if (Math.random() > -1) return;
// usual code
}
I have never seen the compiler remove code like this, however the JIT will effectively remove code which never runs. Assertions is a common use case for this. There may still be a notional cost but generally one not worth worrying about. i.e. less than 1 nano-second.
The cost can be greater if the toString() method of the message is costly. The toString method of a large sql query in a prepared statement caused a heap overflow due to this line. Thus the standard did not remove it.
For testing purposes, I often start typing some code in an already existing project. So, my code I want to test comes before all the other code, like this:
public static void main(String[] args)
{
char a = '%';
System.out.println((int)a);
// To know where '%' is located in the ASCII table.
// But, of course, I don't want to start the whole project, so:
return;
// The real project starts here...
}
But the compiler complains about the return-statement, because of the following "dead code". (While in C++ the compiler obeys the programmer and simply compiles the return statement)
To prevent the compiler complains, I write a stupid if-statement:
if (0 != 1) return;
I hate it. Why can't the compiler do what I ask? Are there some compilation flags or annotations or whatever to solve my problem?
Thanks
There are no flags to turn of this behaviour. The rules that make dead code a compile time error are part of the JLS (§14.21 Unreachable Statements) and can't be turned off.
There's an explicit loophole in the loop which allows code like this:
if (true) return;
someOtherCode(); // this code will never execute, but the compiler will still allow it
This is done explicitly to allow "commenting-out" or conditional compilation (depending on some static final boolean flag).
In case you're curious: the loophole is based on the fact that a known-constant value of the condition expression of an if statement is not considered when checking reachability of code within or after the if statement. A similar situation occurs with while, where known-constant values are considered, so this code will not compile:
while (true) return;
someOtherCode(); // this will be flagged as an unreachable statement
You shouldn't have lots of dead cod ein your project however two ways I get around this for prototyping.
Use /* */ to comment out the code.
// But, of course, I don't want to start the whole project, so:
/*
// The real project starts here...
*/
}
or create a second method.
// But, of course, I don't want to start the whole project, so:
// realProject();
}
public static void realProject()
// The real project starts here...
}