So I recently came accros this. I was creating an if-else-statement with as it's condition a final boolean variable. Eclipse immediatly told me that the else part of the code was unreachable and therefore dead code. This was my code(compacted).
public static void main(String[] args){
final boolean state = true;
if(state == true){
System.out.println("A");
}else{
System.out.println("B");
}
}
Then I though what would happen if the code stayed the same but the variable wasn't final anymore? So I tried that and this was what happened, nothing no warnings or errors. The code:
public static void main(String[] args){
boolean state = true;
if(state == true){
System.out.println("A");
}else{
System.out.println("B");
}
}
Now I'm wondering, why is the first case detected and flagged and the second case not?
Thank you in advance.
Try this as an alternative.
public class Test061 {
public static void main(String[] args){
int a = 0;
if(1 == (a+1)){
System.out.println("A");
}else{
System.out.println("B");
}
}
}
There are still no warnings. Why?
Because the compiler does not execute your code. It only can issue a warning when it sees some "constants" or "constant expressions". Apparently when you put final the compiler then knows that this value cannot change. While if you use "real variables" or "variable expressions", it doesn't know because it doesn't execute your code (to see what the value of state or (a+1) is at this line). So in the latter case, you get no warnings. Hope this makes more sense now.
Think of it this way: the compiler does some code analysis. The first basic pattern is detected by this analysis, while the second pattern is not (probably as it's not that basic).
final means that something cannot be changed, so likely it was flagged because it can never and will never reach that else statement.
On a side note, you never have to do if(Boolean == true) you can just do if(Boolean)
The compiler does some optimization for final variable as shown below and in that case compiler knows that else part will never reached because final variable can't be changed later.
if (true) {
System.out.println("A");
} else {
System.out.println("B");
}
Read more JLS §4.12.4. final Variables
Find more possibilities for Unreachable code compiler error
First example:
When we use final keyword with a variable and assign a value to the variable, then that value can't change again. final boolean state = true; It can't have a value of "false".
Second example:
Here the variable state is not final. It has the possibility to get a value of "false".
So the different behavior is because of the final keyword.
Related
I thought the default value of boolean is false? Why does it print the true statement instead?
My output is goodbye
public class Test {
public static void main (String [] args) {
if(false)
System.out.print("hello");
else System.out.print("goodbye");
}
}
Your code doesn't use the default value of boolean value.
You always print System.out.print("goodbye");, because this section is true.
To achieve this, use the following code
public class Test {
static boolean defaultValue;
public static void main(String[] args) {
System.out.println("Default value is "+defaultValue);
if(defaultValue)
System.out.println("hello");
else
System.out.println("goodbye");
}
}
What sweeper told you in a comment is correct. You seem to be under a wrong impression regarding the syntax I think. Take the following piece of code you gave as an example.
if (false) {
System.out.print("hello");
}
The code inside the if block will never run because the expression false will always evaluate to the boolean value false. You are asking Java to do the following: 'hey run this code if what I put inside the brackets evaluates to true but what you put inside the brackets will always evaluate to false. Thats why java will always run the code inside the else block in your example.
I hope this clears thing up a bit.
Java compiler is smart enough to reset the source code when it generates the class file. Below is an example to understand this. I have created one java file Compiler.java compiler generates class file of this file as Compiler.class.
Compiler .java
public class Compiler {
public static void main(String[] args) {
boolean a = false;
boolean b = true;
{// block1
if (a) {
System.out.println("I am in if");
} else if (b) {
System.out.println("I am in else if");
} else {
System.out.println("I am in else");
}
}
{// block2
if (a) {
} else if (b) {
} else {
}
}
}
}
Compiler .class
public class Compiler {
public static void main(String[] args) {
boolean a = false;
boolean b = true;
if (a) {
System.out.println("I am in if");
} else if (b) {
System.out.println("I am in else if");
} else {
System.out.println("I am in else");
}
if (!a) {
}
}
}
If one can observe the diffence between block1 and block2 in generated class file. One can understand that how java compiler work in smart way. Compiler checks the first “if else if” conditional statments in block1 and finds there are something to execute within these statement(i.e print statements) so it generate block1 as smilar as in java source . In second block , I mean in block2 compiler finds there is nothing to execute within if -esle if statements so it converts and reset the block2 as you can see class file above. Compiler simply converts the “if else if block” to if statement which will be executed or not be executed depending on boolean value.
In second block if statement also contains negation. Could somebody explain this ? Thanks is advance.
Looks like the compiler's seeing there's some code in the first-level 'else' of the block2, and nothing in the matching if, thus reverting the condition to reducing the unnecessary 'if-else' to a simple 'if'.
It then moves on in depth on the second-level if-else, finds the code would do nothing no matter the variables state, thus removing the whole second-level if-else.
At this point, the now-alone first-level 'if' should contain the second-level block code, but that block just was entirely removed and the compiler didn't "come back" to check it again, thus leaving the first 'if' empty.
Is there a feature that lets me check on which path the variable for which I'm getting a "might not be initialised" error is supposedly not initialised? Preferably either native to Java or built into Intellij?
EDIT: managed to reduce my code to a minimal failing example
class MyFailure{
public static void main (String[] args) throws java.lang.Exception{
String test;
boolean abort = false;
while(!abort){
if(false){
abort = true;
continue;
}
test = "stupid";
if(test.equals("stupid")) break;
}
if(!abort){
System.out.println(test);
}
}
}
Main.java:21: error: variable test might not have been initialized
Suppose the while loop reads from a server socket until the buffered value satisfies a certain condition and the if(false) is in fact a check whether the client closed the connection, how would you suggest I handle this?
For the code in your question there is a fairly straightforward solution: use the variable inside the loop where Java too can see it has a value:
public static void main (String[] args) throws java.lang.Exception{
boolean abort = false;
while (!abort){
if (false) {
abort = true;
continue;
}
String test = "stupid";
if (test.equals("stupid")) {
System.out.println(test);
break;
}
}
}
Whether this will work in your real-life code I cannot tell. Consider extracting a method for dealing with the value obtained from the socket and call it from where the System.out.println() is in the example.
Edit: For what it’s worth, the following version compiles too. You may still think that the use of the value (the println) is so deeply nested it reduces readability:
public static void main (String[] args) {
if (! isFalse()) {
String test;
do {
test = "stupid";
} while (! isFalse() && ! test.equals("stupid"));
if (! isFalse()) { // connection still open, so not aborted
System.out.println(test);
}
}
}
private static boolean isFalse() {
return false;
}
As mentioned in the comments I do sometimes — hesitatingly — resort to initializing the variable in question with a nonsensical/bogus value in the declaration so that if that value ever gets used because of a programming error, I will discover. In your example I might use null:
String test = null;
And after the loop:
assert test != null;
so I will catch if it didn’t get a proper value. It’s not a solution I’m happy with, but it happens that I cannot find anything better.
Is there a way to identify whether the following method executed completely or returned halfway through(i.e at line no 3)
static int a=0;
static void test(){
if(a>10){
return;
}
a++;
}
The method was invoked by another method.(a might have been changed by it)
I cannot change the method declaration. I am dealing with an object I created from a java file created by someone else. I am not allowed to change the original file
Your method does almost nothing and no there is no way in this example you gave to know if the method returned before complete execution but if you willing to change the function to a boolean type you can return true at complete execution and false at incomplete.
static boolean test()
{
if(a>10)
return false;
a++;
return true;
}
Run the code under debugger like jdb and set the breakpoint on the internal return statement. If the program stops at this breakpoint, this obviously means that it would return through that statement.
To make things more automated, you could try to launch the debugger and control the debugger from a Java program through Runtime. This would make the approach applicable for more use cases, while not for all.
You could use
void test(int a) {
if (a > 10) {
return;
}
a++;
System.out.println("test executed completely!");
}
Or if you want to use the information programmatically
private boolean executedCompletely;
void test(int a) {
executedCompletely = false;
if (a > 10) {
return;
}
a++;
executedCompletely = true;
}
When you use your test method, you can check whether it ran completely this way:
int initialA = a;
test();
int finalA = a;
if (finalA != initialA) {
//a has been changed, therefore the method ran completely
} else {
//a has not been changed, therefore it was not incremented, therefore the method did not run completely
}
Have a look at this simple Java code:
final class A {
public static void main(String[] args) {
int x = 3;
boolean b;
switch(x) {
case 1:
b = true;
break;
default:
throw new RuntimeException();
}
System.out.println("b: " + b);
}
}
It assigns a b a value in the switch, but in the default case, throws an exception. Of course in real code, x would be computed in a more complex way.
$ javac A.java && java A
Exception in thread "main" java.lang.RuntimeException
at A.main(A.java:10)
Fails when it runs as expected.
One would like to factor this exception throwing into a function to avoid typing the same thing over and over:
final class A {
private static final void f() {
throw new RuntimeException();
}
public static void main(String[] args) {
int x = 3;
boolean b;
switch(x) {
case 1:
b = true;
break;
default:
f();
}
System.out.println("b: " + b);
}
}
However, this doesn't work:
$ javac A.java && java A
A.java:15: variable b might not have been initialized
System.out.println("b: " + b);
^
1 error
It's complaining that b might not be initialized, even though it's clearly even though this is equivalent to the previous code. Why?
The compiler's code reachability analysis is not omniscient.
For example, it does not look inside function calls to check whether they can actually return.
Therefore, it doesn't know that f() will throw an exception, so it think that the println() call is reachable and complains about it.
The Answers of #Slaks and #dasblinkenlight correctly point out the cause of the problem. But they both imply (or could be read as implying) that it is the compiler or compiler writer's fault.
In fact, the Java compiler is only implementing the definite initialization rules as specified in the JLS. It is those rules say that b is not definitely initialized.
If someone was to implement a "smarter" Java compiler that figured out the exception was always thrown ... and allowed the code to compile ... you'd have the problem of one compiler accepting code that another compiler said was illegal. That would be a BAD THING for source code portability.
The bottom line is that your code won't compile because the Java spec says it shouldn't compile.