Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();
Related
I have an Android error handler which never returns (it logs a message and throws an error exception). If I call my error handler from a method which normally returns a value, the Android Studio lint checker reports an error because no value is returned. Is there a way either to tell Android Studio either that my error handler does not return or that the point in the code after calling it is in fact unreachable.
Of course I could put in an unnecessary return statement returning a dummy value of the correct type, but this is inelegant and clutters up my app with an unreachable statement.
I can't find a code inspection to disable to prevent the error, but even if there is one to disable, that would stop it reporting really missing return statements.
Just to repeat, this is not a Java syntax issue. People have said that a Java method must return a value of the type declared. This is
(a) not relevant
(b) not true.
The correct statement is that a Java method, if it returns, must return a value of the declared type. This bit of code
public long getUnsignedLong(String columnName)
throws NumberFormatException, NoColumnException
{
String s = getString(columnName, "getUnsignedLong");
if ((s != null) && s.matches("^[0-9]+$")) {
return Long.parseLong(s);
}
else
{
throw(new NumberFormatException("Bad number " + s));
}
}
is perfectly valid Java, and AS does not complain about it. Indeed if I insert an unnecessary return like this
public long getUnsignedLong(String columnName)
throws NumberFormatException, NoColumnException
{
String s = getString(columnName, "getUnsignedLong");
if ((s != null) && s.matches("^[0-9]+$")) {
return Long.parseLong(s);
}
else
{
throw(new NumberFormatException("Bad number " + s));
}
return 0;
}
AS complains that it is unreachable.
My problem with throwing the exception is that if it actually happens, what my app's user sees is a popup window saying that the app has stopped and asking the user if they want to disable it. This isn't very helpful to the user and isn't very helpful to me when the user reports back to me that it has happened. So instead of throwing the exception I call my fatal error handler which looks like this:-
// Always invoked with fatal = true
// Report a fatal error.
// We send a message to the log file (if logging is enabled).
// If this thread is the UI thread, we display a Toast:
// otherwise we show a notification.
// Then we throw an Error exception which will cause Android to
// terminate the thread and display a (not so helpful) message.
public MyLog(Context context, boolean fatal, String small, String big) {
new Notifier(context, small, big);
new MyLog(context, big, false);
throw(new Error());
}
Yes, I know that argument fatal isn't referenced, but its presence arranges that this particular overload of my error handler is called, and you can see that it throws an exception and doesn't return.
My actual problem is that if I replace the throw in getUnsignedLong by a call to my fatal error handler, which doesn't return, AS complains at the end of getUnsignedLong that it is returning without a value. Well, it's wrong: this point is just as unreachable as it was before. I tried putting a contract in front of MyLog saying that it always fails, but this doesn't help, and pressing right arrow at the AS error report doesn't offer any way of suppressing it. I could put in a dummy return statement or a dummy throw, but either of these would in fact be unreachable code, and I regard this as inelegant and unnecessary.
So my question stands as it was originally asked: how do I tell Android Studio that a method does not return?
Your problem seems to be a Java problem.
In Java a non-void method must return the type it should return. In your case it's the same.
So the simple solution would be to return a dummy value. It's for the sake of the compiler.
The best (harder) solution is to avoid having that kind of construction. If a method normally returns a value, the method will have a return, and in case of an error an exception can occurs. An error handler should handle an error, which means that it's not the default behavior.
Also, you may want to have a look here: unreachable-code-error-vs-dead-code-warning-in-java
If you worry about unit test coverage. When you do unit test, there is always some parts of the code that you can't reach. That's one of the reason why we almost never want a coverage of 100
A method is basically code that you want to access in different ways (easier to call one method in a line than code >50 lines, etc.). When you create the method you either call it:
"void" (never returns an value),
"String" (returns a set of characters/letters),
"int" (returns a number within it's bounds. Integer Types),
"boolean" (returns a 2-type value, true or false).
And more...
In those methods you cand do whatever you want, but make sure that in the end they return the value type specified in the initialization.
Example:
int y = 2;
boolean booleanMethod(){
y = 6;
return true; //or false, doesn't really matter in this case.
}
boolean trueOrFalse(){
if (y == 2) return true;
else return false;
}
//or
void method(int nr){
nr = 10;
}
Those are just some basic examples of methods in Java*, because your problem was a syntax one, not really an AS one.
AS complains that it is unreachable.
Android Studio is correct. Android Studio is correctly implementing the reachability rules in the Java Language Specification.
This is a Java semantics issue. (It is not a syntax issue, but lets not split hairs.)
Lets create a simple but complete example to illustrate why Android Studio is correct:
public class Test {
public int method1() throws Exception {
int result;
method2();
return result;
}
public boolean method2() throws Exception {
throw new Exception("bad stuff");
}
}
$ javac Test.java
Test.java:5: error: variable result might not have been initialized
return result;
^
1 error
(I don't have a copy of Android Studio to hand, but that would give a similar compilation error to javac. Try it.)
Why is this an error? After all, by your reasoning, the return statement should be unreachable.
Here's the problem. That is NOT what the JLS says. In fact, JLS 14.21 says the following, among other things. (These statements are excerpts, and I have added the numbers for clarity. "iff" is an abbreviation that the JLS uses for "if and only if".)
"The block that is the body of a constructor, method, instance initializer, or static initializer is reachable."
"The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable."
"A local variable declaration statement can complete normally iff it is reachable."
"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."
"An expression statement can complete normally iff it is reachable."
Consider the body of method1.
By #1 - the block is reachable
By #2 - the declaration of result is reachable.
By #3 - the declaration can complete normally
By #4 - the call to method2() is reachable
By #5 - the call can return normally
By #4 - the return statement is reachable.
But it is also clear that if we reached the return statement, then result will not have been definitely initialized. (This is obviously true. And JLS 16 bears this out.)
OK so why did they specify Java this way?
In the general case, method1 and method2 can be in separate compilation units; e.g. cases A and B. That means that the methods can be compiled at different times, and only brought together at runtime. Now, if the compiler needs to analyze the body of B.method2 to determine if the return in A.method1 is reachable, then consider what happens if:
the code for B.method2 is modified and B recompiled after compiling A, or
a subclass C of B is loaded in which C.method2 returns normally.
In short, if we need to take account of the flow within of method2 when analyzing reachability in method, we cannot come to a come to a answer at compile time.
Conclusions:
The JLS clearly says / means that the (my) example program is erroneous.
It is not a specification mistake.
If Android Studio (or javac) didn't call the example erroneous, then it wouldn't be a valid implementation of Java.
Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();
I encountered a situation where a non-void method is missing a return statement and the code still compiles.
I know that the statements after the while loop are unreachable (dead code) and would never be executed. But why doesn't the compiler even warn about returning something? Or why would a language allow us to have a non-void method having an infinite loop and not returning anything?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
If I add a break statement (even a conditional one) in the while loop, the compiler complains of the infamous errors: Method does not return a value in Eclipse and Not all code paths return a value in Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
This is true of both Java and C#.
Why would a language allow us to have a non-void method having an infinite loop and not returning anything?
The rule for non-void methods is every code path that returns must return a value, and that rule is satisfied in your program: zero out of zero code paths that return do return a value. The rule is not "every non-void method must have a code path that returns".
This enables you to write stub-methods like:
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
That's a non-void method. It has to be a non-void method in order to satisfy the interface. But it seems silly to make this implementation illegal because it does not return anything.
That your method has an unreachable end point because of a goto (remember, a while(true) is just a more pleasant way to write goto) instead of a throw (which is another form of goto) is not relevant.
Why doesn't the compiler even warn about returning something?
Because the compiler has no good evidence that the code is wrong. Someone wrote while(true) and it seems likely that the person who did that knew what they were doing.
Where can I read more about reachability analysis in C#?
See my articles on the subject, here:
ATBG: de facto and de jure reachability
And you might also consider reading the C# specification.
The Java compiler is smart enough to find the unreachable code ( the code after while loop)
and since its unreachable, there is no point in adding a return statement there (after while ends)
same goes with conditional if
public int get() {
if(someBoolean) {
return 10;
}
else {
return 5;
}
// there is no need of say, return 11 here;
}
since the boolean condition someBoolean can only evaluate to either true or false, there is no need to provide a return explicitly after if-else, because that code is unreachable, and Java does not complain about it.
The compiler knows that the while loop will never stop executing, hence the method will never finish, hence a return statement is not necessary.
Given your loop is executing on a constant - the compiler knows that it's an infinite loop - meaning the method could never return, anyway.
If you use a variable - the compiler will enforce the rule:
This won't compile:
// Define other methods and classes here
public int doNotReturnAnything() {
var x = true;
while(x == true) {
//do something
}
//no return statement - won't compile
}
The Java specification defines a concept called Unreachable statements. You are not allowed to have an unreachable statement in your code (it's a compile time error). You are not even allowed to have a return statement after the while(true); statement in Java. A while(true); statement makes the following statements unreachable by definition, therefore you don't need a return statement.
Note that while Halting problem is undecidable in generic case, the definition of Unreachable Statement is more strict than just halting. It's deciding very specific cases where a program definitely does not halt. The compiler is theoretically not able to detect all infinite loops and unreachable statements but it has to detect specific cases defined in the specification (for example, the while(true) case)
The compiler is smart enough to find out that your while loop is infinite.
So the compiler cannot think for you. It cannot guess why you wrote that code. Same stands for the return values of methods. Java won't complain if you don't do anything with method's return values.
So, to answer your question:
The compiler analyzes your code and after finding out that no execution path leads to falling off the end of the function it finishes with OK.
There may be legitimate reasons for an infinite loop. For example a lot of apps use an infinite main loop. Another example is a web server which may indefinitely wait for requests.
In type theory, there is something called the bottom type which is a subclass of every other type (!) and is used to indicate non-termination among other things. (Exceptions can count as a type of non-termination--you don't terminate via the normal path.)
So from a theoretical perspective, these statements that are non-terminating can be considered to return something of Bottom type, which is a subtype of int, so you do (kind of) get your return value after all from a type perspective. And it's perfectly okay that it doesn't make any sense that one type can be a subclass of everything else including int because you never actually return one.
In any case, via explicit type theory or not, compilers (compiler writers) recognize that asking for a return value after a non-terminating statement is silly: there is no possible case in which you could need that value. (It can be nice to have your compiler warn you when it knows something won't terminate but it looks like you want it to return something. But that's better left for style-checkers a la lint, since maybe you need the type signature that way for some other reason (e.g. subclassing) but you really want non-termination.)
There is no situation in which the function can reach its end without returning an appropriate value. Therefore, there is nothing for the compiler to complain about.
Visual studio has the smart engine to detect if you have typed a return type then it should have a return statement with in the function/method.
As in PHP Your return type is true if you have not returned anything. compiler get 1 if nothing has returned.
As of this
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Compiler know that while statement itself has a infinte nature so not to consider it. and php compiler will automatically get true if you write a condition in expression of while.
But not in the case of VS it will return you a error in the stack .
Your while loop will run forever and hence won't come outside while; it will continue to execute. Hence, the outside part of while{} is unreachable and there is not point in writing return or not. The compiler is intelligent enough to figure out what part is reachable and what part isn't.
Example:
public int xyz(){
boolean x=true;
while(x==true){
// do something
}
// no return statement
}
The above code won't compile, because there can be a case that the value of variable x is modified inside the body of while loop. So this makes the outside part of while loop reachable! And hence compiler will throw an error 'no return statement found'.
The compiler is not intelligent enough (or rather lazy ;) ) to figure out that whether the value of x will be modified or not. Hope this clears everything.
"Why doesn't the compiler even warn about returning something? Or why would a language allow us to have a non-void method having an infinite loop and not returning anything?".
This code is valid in all other languages too (probably except Haskell!). Because the first assumption is we are "intentionally" writing some code.
And there are situations that this code can be totally valid like if you are going to use it as a thread; or if it was returning a Task<int>, you could do some error checking based on the returned int value - which should not be returned.
I may be wrong but some debuggers allow modification of variables. Here while x is not modified by code and it will be optimized out by JIT one might modify x to false and method should return something (if such thing is allowed by C# debugger).
The specifics of the Java case for this (which are probably very similar to the C# case) are to do with how the Java compiler determines if a method is able to return.
Specifically, the rules are that a method with a return type must not be able to complete normally and must instead always complete abruptly (abruptly here indicating via a return statement or an exception) per JLS 8.4.7.
If a method is declared to have a return type, then a compile-time
error occurs if the body of the method can complete normally.
In other words, a method with a return type must return only by using
a return statement that provides a value return; it is not allowed to
"drop off the end of its body".
The compiler looks to see whether normal termination is possible based on the rules defined in JLS 14.21 Unreachable Statements as it also defines the rules for normal completion.
Notably, the rules for unreachable statements make a special case just for loops that have a defined true constant expression:
A while statement can complete normally iff at least one of the
following is true:
The while statement is reachable and the condition expression is not a
constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
So if the while statement can complete normally, then a return statement below it is necessary since the code is deemed reachable, and any while loop without a reachable break statement or constant true expression is considered able to complete normally.
These rules mean that your while statement with a constant true expression and without a break is never considered to complete normally, and so any code below it is never considered to be reachable. The end of the method is below the loop, and since everything below the loop is unreachable, so is the end of the method, and thus the method cannot possibly complete normally (which is what the complier looks for).
if statements, on the other hand, do not have the special exemption regarding constant expressions that are afforded to loops.
Compare:
// I have a compiler error!
public boolean testReturn()
{
final boolean condition = true;
if (condition) return true;
}
With:
// I compile just fine!
public boolean testReturn()
{
final boolean condition = true;
while (condition)
{
return true;
}
}
The reason for the distinction is quite interesting, and is due to the desire to allow for conditional compilation flags that do not cause compiler errors (from the JLS):
One might expect the if statement to be handled in the following
manner:
An if-then statement can complete normally iff at least one of the
following is true:
The if-then statement is reachable and the condition expression is not
a constant expression whose value is true.
The then-statement can complete normally.
The then-statement is reachable iff the if-then statement is reachable
and the condition expression is not a constant expression whose value
is false.
An if-then-else statement can complete normally iff the then-statement
can complete normally or the else-statement can complete normally.
The then-statement is reachable iff the if-then-else statement is
reachable and the condition expression is not a constant expression
whose value is false.
The else-statement is reachable iff the if-then-else statement is
reachable and the condition expression is not a constant expression
whose value is true.
This approach would be consistent with the treatment of other control
structures. However, in order to allow the if statement to be used
conveniently for "conditional compilation" purposes, the actual rules
differ.
As an example, the following statement results in a compile-time
error:
while (false) { x=3; } because the statement x=3; is not reachable;
but the superficially similar case:
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.
Why does the conditional break statement result in a compiler error?
As quoted in the loop reachability rules, a while loop can also complete normally if it contains a reachable break statement. Since the rules for the reachability of an if statement's then clause do not take the condition of the if into consideration at all, such a conditional if statement's then clause is always considered reachable.
If the break is reachable, then the code after the loop is once again also considered reachable. Since there is no reachable code that results in abrupt termination after the loop, the method is then considered able to complete normally, and so the compiler flags it as an error.
I'm having a hard time finding information about javac's code elimination capabilities:
I read that if you have something like the following, the if-statement will be eliminated:
static final boolean DEBUG = false;
if (DEBUG) System.out.println("Hello World!"); // will be removed
But how about this, for example:
static final int VALUE = 3;
if (VALUE > 9) System.out.println("VALUE > 9 ???"); // will this be removed?
Or this:
static final SomeEnum VALUE = SomeEnum.FOO;
if (VALUE==SomeEnum.BAR) System.out.println("Bar???"); // will this be removed?
Since it's very difficult/impossible to analyze a program to find all dead code (probably similar to the halting problem), I would imagine that there are only a few well-defined constructs (like the first example above), which javac will recognize and remove reliably. Is there a comprehensive list of these constructs?
assylias seems to have found the answer (let me just put it all together):
Chapter "14.21. Unreachable Statements" of the JLS specifies, that, in general, any unreachable statement in the code is considered a compile-time-error, with the only exception being a special treatment of if-statements to specifically allow for conditional compiles.
Therefore, the only construct that may result in code elimination (if the compiler chooses to do so!) is:
if (compileTimeConstantExpression) {
doThis(); // may be removed if compileTimeConstantExpression == false;
} else {
doThat(); // may be removed if compileTimeConstantExpression == true;
}
(the else-part is optional, of course)
All other constructs that would allow for code elimination, like for example while (false) ..., are disallowed and cause a compile-time-error instead rather than resulting in conditional compilation.
The definition for what constitutes an acceptable compileTimeConstantExpression can be found in chapter "15.28. Constant Expressions" of the JLS. Another great page with further examples can be found here: Compile Time Constants in Java
Note: There is no requirement for a compiler to remove the "unreachable" sections of an if-stament. javac seems to do this reliably, but other compilers may not. The only way to know for sure is to check the output via decompilation, for example using javap -c as suggested by Jon Skeet.
I have run a few tests and it seems (logically) that javac removes the code iif the condition is a constant expression that evaluates to false.
In summary, constant expressions are expressions that only use constants as operands, i.e. primitives, string literals and final primitives or Strings variables that have been initialised with a constant value.
Note that this is compiler dependent as the JLS does not force the compiler to be that smart as explained at the very bottom of 14.21:
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.
I see that for 2nd example it gets removed too
this was my class
public class Test {
public static void main(String[] args) throws Exception{
final int VALUE = 3;
if (VALUE > 9) System.out.println("VALUE > 9 ???");
}
}
and this is decompiled version
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: return
}
My guess would be that this is implementation specific (Oracle, IBM,...).
If you're interested in Oracle's version, a good place to start looking for resources would be the OpenJDK project:
http://openjdk.java.net/groups/compiler/
In some of our companys projects code I often read something like this:
boolean foo = Boolean.FALSE;
Besides the fact that AFAIK I only have to initialize local variables in Java at all (no random values like in Pascal) and besides the fact that especially for booleans I often WANT to have an initialization, what do I miss here? Why not:
boolean foo = false;
I don't get it. And code analyzation tools like PMD and Findbugs mark it, too. But why?
Edit:
Without really knowing much about the bytecode except that it is there I created an example class and decompiled it. The Boolean.FALSE went to:
0: getstatic #15 // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
3: invokevirtual #21 // Method java/lang/Boolean.booleanValue:()Z
6: istore_1
The 'false' variant went to:
0: iconst_1
1: istore_1
So without knowing too much about this, I'd guess that more statements means more time to execute so it's not only wrong but also slower in the long run.
boolean foo = Boolean.FALSE;
This is strange and unnecessarily complicated code, written by someone who likely didn't know Java very well. You shouldn't write code like this, and PMD and FindBugs are right to mark this.
Boolean.FALSE is a java.lang.Boolean object that gets auto-unboxed; the compiler essentially translates this to:
boolean foo = Boolean.FALSE.booleanValue();
I don't have to initialize variables in Java at all...
Member variables do not need to be initialized explicitly; if you don't, they'll be initialized with a default value (which is false in the case of boolean). Local variables do need to be explicitly initialized; if you try to use a local variable without initializing it, the compiler will give you an error.
Both are same. but boolean foo = false; is enough.
There is no good reason to do this, it was probably just a novice Java programmer. I wouldn't worry too much, just replace it with false.
At the same time, you can usually if not always arrange your code such that you never declare a variable you don't have the final value for, i.e., making your objects immutable, which makes them easier to think about. What's the value of x? compared to What's the value of x between the calls to foo() and bar()? The first is generally easier to answer. This requires you to split up your classes along lines you might not be used to doing but I recommend at least trying it out.
The style to use an auto-unboxed Boolean constant in fact meshes well with the overall oververbosity endemic to many Java projects. For example:
public boolean isItOrIsItNotTheValueWeExpect(String aStringParameterThatCouldBeNull) {
boolean booleanReturnValue = Boolean.FALSE;
if (aStringParameterThatCouldBeNull != null) {
if (aStringParameterThatCouldBeNull.length() > 3) {
booleanReturnValue = Boolean.TRUE;
}
else {
booleanReturnValue = Boolean.FALSE;
}
}
else if (aStringParameterThatCouldBeNull == null) {
booleanReturnValue = Boolean.TRUE.booleanValue();
}
return booleanReturnValue;
}
Obviously, the code above would be much preferred to this unreadable mess:
public boolean validate(String s) {
return s == null? true : s.length() > 3;
}
The very occurrence of a ternary operator is considered a transgression and some projects even have it flagged by CheckStyle.
If your projects conform to such stylistic guidelines as these, that could justify your suspicious line of code.
There is no difference really although the 1st method won't work on a 1.4 or earlier JVM. The first is more convoluted since it is fetching the static value from the Boolean object and then relying on autoboxing (introduced in 1.5) to change it from a Boolean object to a boolean primitive) although I can't imagine it would ever make any speed difference.
Generally though if you are assuming a particular initial value for a variable then I would recommend initialising it rather than just declaring it as it makes the code more readable.