final int a = 1;
final int b;
b = 2;
final int x = 0;
switch (x) {
case a:break; // ok
case b:break; // compiler error: Constant expression required
}
/* COMPILER RESULT:
constant expression required
case b:break;
^
1 error
*/
Why am I getting this sort of error? If I would have done final int b = 2, everything works.
The case in the switch statements should be constants at compile time. The command
final int b=2
assigns the value of 2 to b, right at the compile time. But the following command assigns the value of 2 to b at Runtime.
final int b;
b = 2;
Thus, the compiler complains, when it can't find a constant in one of the cases of the switch statement.
b may not have been initialized and it is possible to be assigned multiple values. In your example it is obviously initialized, but probably the compiler doesn't get to know that (and it can't). Imagine:
final int b;
if (something) {
b = 1;
} else {
b = 2;
}
The compiler needs a constant in the switch, but the value of b depends on some external variable.
The final variable without value assigned to it is called a blank variable. A blank final can only be assigned once and must be unassigned when an assignment occurs or once in the program.
In order to do this, a Java compiler runs a flow analysis to ensure that, for every assignment to a blank final variable, the variable is definitely unassigned before the assignment; otherwise a compile-time error occurs
That is why when the compiler compiles the switch construct it is throwing constant expression required because the value of b is unknown to the compiler.
Related
A.
int a=10,b=20;
for(int i=0;a>b;i++)
{
System.out.print("aa");
}
System.out.print("bb");
B.
final int a=10,b=20;
for(int i=0;a>b;i++)
{
System.out.print("aa");
}
System.out.print("bb");
Q. why code A prints an output "bb" while code B returns an error "unreachable code"?
final int a=10,b=20;
means the variables will never Change its values after compiled
therefore this for Loop
for(int i=0;a>b;i++)
has a never breaking condition (a>b will never be true), therefore the Loop is forever and this becomes
System.out.print("bb");
"unreachable code"
On the Section A code can be changed like below and print both outputs. Note that while execute on the loop it's possible to change the value on a and b. and output will be aabb
int a=10,b=20;
for(a=30;a>b;)
{
System.out.print("aa");
a=a-20;
}
System.out.print("bb");
In section B on the run time it's not possible to change the values of a or b since those are Final variable. Due to that reason JVM can identify that there is no possibility of a>b condition getting true. (Since a>b always false).
So JVM will throw compilation error saying Unreachable code.
final key word says that a and b values cannot be changed i.e. they are values and not variables and therefore for loop will never execute inner statement, so the compiler deduces that the code inside for loop is unreachable.
B returns an error "unreachable code"
for(int i=0;a>b;i++) //with final modifier, `a` & `b` are constants
How compiler sees your code with final modifier, that's compiler complains about
for(int i=0;10>20;i++)
10 will be always less than 20, so your body of loop in unreachable code
In a method I have this:
int x = 0
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
}
if (x != 0) {
doLater(() -> showErrorMessage(x)); // compile error here
}
// no more reference to 'x' here
I don't understand why it produces compilation error. The error says that x is not final or effectively-final, so it can't be accessed from the lambda body. There is no modification to x after the doLater call, so the value of x is actually already determined when doLater is called.
I am guessing that the answer to this question is because x is not qualified to be called an effectively-final variable. However, I want to know what the reason is.
Can't the compiler just create a temporary final variable, effectively making the code like:
if (x != 0) {
final int final_x = x;
doLater(() -> showErrorMessage(final_x));
}
and everything still work the same?
Effectively final means that it could have been made final i.e. it never changes. It means that effectively, the variable could be a final one.
The problem is that it doesn't keep track of the last time you changed it, but rather, did you ever change it. Change your if statement to
int x;
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
} else {
x = 0;
}
or
int x = isA() ? 1 :
isB() ? 2 : 0;
Your x variable would have been effectively final it it was initialized once and not changed again under any circumstances. If you had only:
int x = 0;
doLater(() -> showErrorMessage(x));
then it would have compiled.
However, adding conditions that might change the variable's value
int x = 0;
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
}
makes the variable being not effectively final and thus the compile error is risen.
Additionally, since this pointer approach you've implemented wouldn't work, you could refactor your code a bit to a simple if-else statement:
if (isA()) {
doLater(() -> showErrorMessage(1));
} else if (isB()) {
doLater(() -> showErrorMessage(2));
}
and completely get rid of x.
Short version, a variable is effectively final if it is assigned exactly once, no matter which code path is executed.
Long version, quoting Java Language Specification 4.12.4. final Variables (emphasis mine):
Certain variables that are not declared final are instead considered effectively final:
A local variable whose declarator has an initializer (§14.4.2) is effectively final if all of the following are true:
It is not declared final.
It never occurs as the left hand side in an assignment expression (§15.26). (Note that the local variable declarator containing the initializer is not an assignment expression.)
It never occurs as the operand of a prefix or postfix increment or decrement operator (§15.14, §15.15).
Now, you can make it effectively final by removing the initializer, because it continues:
A local variable whose declarator lacks an initializer is effectively final if all of the following are true:
It is not declared final.
Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right hand side of the assignment expression (§16 (Definite Assignment)).
It never occurs as the operand of a prefix or postfix increment or decrement operator.
I am a little bit confused about the following piece of code:
public class Test{
int x = giveH();
int h = 29;
public int giveH(){
return h;
}
public static void main(String args[])
{
Test t = new Test();
System.out.print(t.x + " ");
System.out.print(t.h);
}
}
The output here is 0 29, but I thought that this has to be a compiler error, because the variable h should have not been initialized when it comes to the method giveH(). So, does the compilation go through the lines from top to bottom? Why is this working? Why is the value of x 0 and not 29?
The default value of int is 0 (see here). Because you initialize x before h, giveH will return the default value for a int (eg 0).
If you switch the order like this
int h = 29;
int x = giveH();
the output will be
29 29
Compilation in Java doesn't need the method to be declared before it is used. The Java tutorial goes into a bit more detail on initialization.
Here's a way to think about it: the compiler will make a note to look for a method called giveH somewhere in scope, and it will only error if it leaves the scope and doesn't find it. Once it gets to the giveH declaration then the note is resolved and everyone is happy.
Also, the variable initialization for instance variables in Java is moved to the beginning of the constructor. You can think of the lines above being split into two parts, with the declaration for x and h above, and the assignment inside the constructor.
The order of declaration does matter in this case. When the variable x is initialized, h has the default value of 0, so giveH() will return that default value. After that, the variable h is given the value 29.
You can also look at the Java Language Specification sections on Field Initialization and Forward References During Field Initialization.
#Maloubobola has provided the correct answer, but since you don't seem to fully understand yet, let me try to elaborate.
When Test is created, it runs the variable initialization (x = giveH(), h= 29) once. Your misunderstanding may be that variable x is always determined by giveH(), whereas it only determines it's value when x is initialized.
That's why the order of the statements is crucial here; x is initialized before h, and therefore h is 0 when giveH() is called at x's initialization.
It's bad practice to use a method in the field initializer. You can fix this by making h final. Then it will be initialized when the class is loaded.
import java.util.ArrayList;
public class Test {
int x = giveH();
final int h=29;
final public int giveH(){
return h;
}
public static void main(String args[]) {
Test t = new Test();
System.out.print(t.x + " ");
System.out.print(t.h);
}
}
If we do not initialize the value for non static variables like x in this program JVM will provide default value zero for class level non static variables.
Once declared, all the array elements store their default values.
Elements in an array that store objects default to null.
Elements of an array that store primitive data types store:
0 for integer types (byte, short, int,long);
0.0 for decimal types (float and double);
false for boolean;
\u0000 for char data.
Actauls values will come only when we give initialize them
Have a look at this simple Java code:
class A {
public static void main(String[] args) {
final int x;
try {
throw new RuntimeException();
x = 1;
} finally {}
x = 2;
System.out.println("x: " + x);
}
}
I'd expect it to print "x: 2".
A.java:6: unreachable statement
x = 1;
^
A.java:8: variable x might already have been assigned
x = 2;
^
2 errors
It says it wont compile because on line 8, x = 2 might reassign the final variable, but this is false because as it said above, the line x = 1 is unreachable, thus it will assign it for the first time, not reassign.
Why does the compiler give an error stating that "x might have already been assigned" when it knows that x has not been assigned?
It is explained in the JLS chapter 16
[...] Similarly, every blank final variable must be assigned at most
once; it must be definitely unassigned when an assignment to it
occurs.
Such an assignment is defined to occur if and only if either the
simple name of the variable (or, for a field, its simple name
qualified by this) occurs on the left hand side of an assignment
operator.
For every assignment to a blank final variable, the variable must be
definitely unassigned before the assignment, or a compile-time error
occurs.
So, JLS does not seem to care about unreachable code.
And concerning exceptions it says:
An exception parameter V of a catch clause (§14.20) is definitely
assigned (and moreover is not definitely unassigned) before the body
of the catch clause.
so the problem here is that x=1 and x=2 are both definitely assigned as
If a try statement does have a finally block, then these rules also
apply:
V is definitely assigned after the try statement iff at least one of the following is true:
V is definitely assigned after the try block and V is definitely assigned after every catch block in the try statement.
V is definitely assigned after the finally block.
V is definitely unassigned after a try statement iff V is definitely unassigned after the finally block.
The first error is a result of, well, unreachable code. Once you throw the exception, the method is halted, and the next line can never be executed. You can fix the second error by simply removing the final modifier from x. But I must ask, why are you writing a program whose only purpose is to throw a RuntimeException?
The java compiler can't look at things the same way we do as humans. It doesn't see cause and effect, only what is in error. And in this case it may be a good thing, because even if you fix one of the errors, the other will persist.
It's probably a language definition issue. One area of the spec prohibits reassignment if it might have already been assigned. A different area discusses unreachable code.
The combination of the two is probably never really addressed.
Could you perhaps supply something that is a bit more representative of what you are really trying to accomplish?
Looks like you are learning Java. The first error is because the compiler can see that execution of a block cannot continue after you throw an exception: control will jump to wherever the exception is caught. Other posters have explained the second error.
I've seen other cases that are less straightforward, for example something like:
final int x;
boolean b = ...;
if(b) {
x = 1;
}
...
if(!b) {
x = 2;
}
The easiest solution is to assign to a temporary non-final variable, then copy it to the final variable:
final int x;
int _x = 0;
boolean b = ...;
if(b) {
_x = 1;
}
...
if(!b) {
_x = 2;
}
x = _x;
In Java Language Spex 15.7:
Code is usually clearer when each expression contains at most one side
effect, as its outermost operation
What does it mean?
It means that each expression should do one task at a time.
Consider the following two declarations:
int a = 10;
int b = 20;
Now the task is to add these two ints and increment b by 1. There are two way to do it.
int c = a + b++;
and
int c = a + b;
b++;
JLS prefers and recommends the latter one.
What this means is that:
int x = someFunction(a, b);
is clearer when someFunction(a, b) doesn't have any side-effect i.e. it doesn't change anything. Rather the only change in the above is the assignment to x.
Another example would be use of prefix/postfix incrementers.
int x = a + b;
is clearer than
int x = (a++) + (++b);
since only x is assigned to. In the second example a and b are changed in the same statement.
By limiting the side effects, you can more easily reason about the functioning of the code, and/or re-order statement invocations, including parallelising them e.g. in the below, if the methods don't have side-effects, then you can invoke the methods a(), b() and c() representing the arguments in any order, and/or in parallel.
int res = f(a(), b(), c());
Side-effect of an expression is mostly an assignment to variable during evaluation of the expression.
Notice the code:
int x = 5, y = 7;
while ((z = x-- + --y) > 0 ) {
console.out("What is 'z' now? " + z);
console.out("How many times will this be printed?");
}
Evaluation of the condition has 3 side-effects:
decrementing x
decrementing y
assignment to z
Looks twisted, isn't it?