Why can't variables be declared in an if statement? - java

The following Java code does not compile.
int a = 0;
if(a == 1) {
int b = 0;
}
if(a == 1) {
b = 1;
}
Why? There can be no code path leading to the program assigning 1 to b without declaring it first.
It occurred to me that b's variable scope might be limited to the first if statement, but then I wouldn't understand why. What if I really don't want to declare b needlessly, in order to improve performance? I don't like having variables left unused after declaration.
(You may want to argue than I could simply declare b in the second if statement, in that case, just imagine that it could be in a loop somewhere else.)

Variables can be declared inside a conditional statement. However you try and access b in a different scope.
When you declare b here:
if(a == 1) {
int b = 0;
}
It is only in scope until the end }.
Therefore when you come to this line:
b = 1;
b does not exist.

Why? There can be no code path leading to the program assigning 1 to b without declaring it first.
You are right, but the compiler doesn't know that. The compiler does not execute the code. The compiler only translates to bytecode without evaluating expressions.

This { } defines a block scope. Anything declared between {} is local to that block. That means that you can't use them outside of the block. However Java disallows hiding a name in the outer block by a name in the inner one. This is what JLS says :
The scope of a local variable declaration in a block (§14.2) is the rest of the block in which the declaration appears, starting with its own initializer (§14.4) and including any further declarators to the right in the local variable declaration statement.
The name of a local variable v may not be redeclared as a local variable of the directly enclosing method, constructor or initializer block within the scope of v, or a compile-time error occurs.

Its all about java variable scoping.
You'll need to define the variable outside of the if statement to be able to use it outside.
int a = 0;
int b = 0;
if(a == 1) {
b = 1;
}
if(a == 1) {
b = 2;
}
See Blocks and Statements

It is a local variable and is limited to the {} scope.
Try this here.

you have declared b variable inside if block that is not accessible out side the if block and if you want to access then put outside if block

The scope of b is the block it is declared in, that is, the first if. Why is that so? Because this scoping rule (lexical scoping) is easy to understand, easy to implement, and follows the principle of least surprise.
If b were to be visible in the second if:
the compiler would have to infer equivalent if branches and merge them to a single scope (hard to implement);
changing a condition in a random if statement would potentially make some variables visible and others hidden (hard to understand and source of surprising bugs).
No sane language has such a complicated scoping rule.
w.r.t. performance - declaring an extra variable has a negligible impact on performance. Trust the compiler! It will allocate registers efficiently.

Because when b goes out of scope in the first if (a == 1) then it will be removed and no longer exists on the stack and therefore can not be used in the next if statement.

{ } is used to define scope of variables.And here you declared :
if(a == 1)
{
int b = 0;
}
So here scope of b will be only in { }.So you are using variable b outside { }, it is giving compilation error.
You can also refer this:
http://docs.oracle.com/javase/tutorial/java/javaOO/variables.html

If you re declaring variable inside a block then the limitation of the variable limits to the particular block in which it got declared.
NOTE : Only static variables has access from anywhere in the program.
In you code :
int a = 0;
if(a == 1) {
int b = 0;
}
if(a == 1) {
b = 1;
}
variable 'a' can be accessed in any if statement as its declare outside the block but, variable 'b' is declare inside if hence limited its use outside the block.
If you want to use 'b' outside the if statement you have to declare it where you have declare variable 'a'.

int a = 0;
if(a == 1) {
int b = 0; // this int b is only visible within this if statement only(Scope)
}
if(a == 1) {
b = 1; // here b can't be identify
}
you have to do following way to make correct the error
int a = 0;
int b = 0;
if(a == 1) {
b=0;
}
if(a == 1) {
b = 1;
}

Just for completeness sake: this one works as well (explanation is scoping, see the other answers)
int a = 0;
if(a == 1) {
int b = 0;
}
if(a == 1) {
int b = 1;
}
Due to scoping, b will only be accessible inside the if statements. What we have here are actually two variables, each of which is just accessible in their scope.

variable b's scope is only until the if block completes, as this is where you declared the variable. That is why it cannot be accessed on the following block. This is for memory allocation, otherwise they would be ALOT of variables floating around in the memory.
int a = 0;
if(a == 1) {
int b = 0;
} //b scope ends here
if(a == 1) {
b = 1; //compiler error
}

Related

Using effectively final variable

Question related to frames of function and effective finality of variables.
int lenMin = list.get(0).length();
for (String s : list)
if (s.length() < lenMin)
lenMin = s.length();
list
.stream()
.filter(s -> lenMin == s.length())
.forEach(System.out::println);
st:1. here we create a variable.
st: 3-5 here we do something with it (change it).
st:9 the lenMin variable is underlined in red.
Effectivity finality we cannot change, but can we use it? I know that the lambda has a new frame, but if we do this:
int len = lenMin;
list
.stream()
.filter(s -> len == s.length())
.forEach(System.out::println);
then there will be no error.
Please explain why this is happening?
Functions that can close over variables need a way to capture those variables.
If a variable can't change, then you can easily capture variables by copying their values.
Java does exactly that. Java lambdas, and inner classes simply correspond to classes that have an extra field for each captured variable.
The JVM only understands flat classes; nested and inner classes are compiler fictions.
Runnable f() {
int answer = 42;
class C extends Runnable {
public void run() { System.out.println(answer); }
}
return new C();
}
compiles to something equivalent to
class C {
private int answer; // Capturing field
C(int answer) { this.answer = answer }
void run() { System.out.println(this.answer); }
}
Runnable f() {
int answer = 42;
return new C(answer);
}
This change is called closure conversion.
As long as answer is only assigned once, javac can do simple closure conversion: copying answer into any classes and lambdas that need it.
But simple copying doesn't work if a local variable might change after being captured.
int answer = 42;
Runnable c = new C();
answer += 1;
c(); // Should this print 42 or 43?
Programmers familiar with languages that allow for reassignment of closed-over variables would get confused if the above didn't behave like the JavaScript below.
let answer = 42;
let c = () => console.log(answer);
answer += 1;
c(); // Logs 43
Java could allow for mutable closed-over variables by doing more complicated closure conversion. For example, instead of copying answer, storing answer in a shared object and rewriting reads&writes to be reads/writes of a property or element of that shared object.
For example,
int[] answerBox = new int[1];
answerBox[0] = 42; // assignment
...
answerBox[0] = 43; // re-assignment
...
System.out.println(answerBox[0]);
...
Java's designers decided not to do that though; capturing by-reference the way JavaScript does would introduce a host of subtle complications. JavaScript is single threaded, but the JVM is not, so imagine when a closed over long variable is suddenly assigned in more than one thread.
This is not hard to work around though as long as you don't need to mutate captured state after creating the capturing value.
Runnable f() {
int x = 1;
x += 1;
return () -> System.out.println(x); // ERROR
}
The above fails, but the below works; we simply store the state of the non-effectively-final x in an effectively final finalX that the lambda can capture.
Runnable f() {
int x = 1;
x += 1;
int finalX = x;
return () -> System.out.println(finalX); // OK
}
So the question is: why one has to introduce a new copy, to have a variable, that is effectively final (= could be made final). Here len.
int len = lenMin;
list
.stream()
.filter(s -> len == s.length())
.forEach(System.out::println);
The reason that the lambda (inside filter) is an anonymous implemented interface instance, that uses len as follows: as it is a local variable, it copies it into a local interface variable with the same name len.
But now one has two variables (though same names). As the lambda theoretically could run in another thread, one has two threads that can change same named variables differently. The language designers did not want to allow two lens with different values, and simply required that the variable is not changed, effectively final.
Here it would not be dangerously misleading, but assume you later assign to len at the end or inside a lambda, and perhaps use .parallelStream(). Then suddenly a compile error must be given at an unchanged piece of code.
That is an additionally sufficient reason to keep the rule simple: a local variable used inside a lambda must be effectively final.

if we declare a variable before a for loop and we are declaring same name variable in for loop statement in java. we are getting error. why? [duplicate]

I got this situation I can't understand about shadowing. For example the following code:
class Foo {
int a = 5;
void goFoo(int a) {
// No problem naming parameter as same as instance variable
for (int a = 0; a < 5; a++) { }
// Now the compiler complains about the variable a on the for loop
// I thought that the loop block had its own scope so I could shadow
// the parameter, why the compiler didn't throw an error when i named
// the parameter same as the instance variable?
}
}
You can make a local variable shadow an instance/static variable - but you can't make one local variable (your loop counter) shadow another local variable or parameter (your parameter).
From the Java Language Specification, section 14.4.3:
If a name declared as a local variable is already declared as a field name, then that outer declaration is shadowed (§6.3.1) throughout the scope of the local variable.
Note the "field name" part - it's specifying that it has to be a field that is shadowed.
And from section 8.4.1:
The scope of a parameter of a method (§8.4.1) or constructor (§8.8.1) is the entire body of the method or constructor.
These parameter names may not be redeclared as local variables of the method, or as exception parameters of catch clauses in a try statement of the method or constructor.
(It goes on to talk about local classes and anonymous classes, but they're irrelevant in your case.)
void goFoo(int a) {
for (int a = 0; a < 5; a++) { }
}
it is similar to
void goFoo() {
int a;
for (int a = 0; a < 5; a++) { }
}
so multiple declaration of a on the same scope, it is not acceptable.
or simply it is similar to
void goFoo() {
int a;
int a;
}
Also See
java-variable-scope-shadowing
The scope of the variable depends on the hierarchy of the block as well.
ie if u use like this
void goFoo(int a) {
// No problem naming parameter as same as instance variable
for (int b = 0; b < 5; b++) { }
//Now the compiler complains about the variable a on the for loop
// i thought that the loop block had its own scope so i could shadow
// the parameter, why the compiler didnt throw an error when i named
// the parameter same as the instance variable?
int b; // you can do this.
}
That is if a variable is declared in the outer block then you can not declare the same in the block which is inner. the other way you can do it.
But you don't declare the second "a" in that new scope, as your code shows. It's in the scope of the goFoo() block itself.
The problem isn't that the loop is shadowing the class field, the name is already used by the parameter.
Two options: One is to change the loop:
for (a = 0; a < 5; a++) { }
This uses the parameter as the index variable. Not clear why you would have a parameter, but all the same...
The other option is to rename the loop variable or the parameter to something else.
It is not shadowing, it is a conflict here. Both a are in the method scope. One cannot define two variables of same name in the same scope.
In Java (unlike, say, in c++) you cannot declare a local variable when another local variable with the same name is "in scope".
You cannot do this in Java
void foo() {
int a;
{
int a; // You cannot declare 'a' here because a
// from the outer block is still in scope
// here. Local variables in Java cannot be
// shadowed by another local variable with
// the same name. Only instance variables
// can be shadowed by the local variables
// with the same name.
}
}
However c++ allows you to do this
void foo()
{
int a;
a = 10;
cout << a << endl; // prints 10 duh
{
int a; // this declaration shadows the local
// variable 'a' from the outer scope.
a = 20; // assigns 20 to the variable 'a' in
// current inner scope, like you'd expect.
cout << a << endl; // prints 20.
}
cout << a << endl; // prints 10
}

try catch java -- java complaining that variable may not be initialized [duplicate]

When I try to compile this:
public static Rand searchCount (int[] x)
{
int a ;
int b ;
...
for (int l= 0; l<x.length; l++)
{
if (x[l] == 0)
a++ ;
else if (x[l] == 1)
b++ ;
}
...
}
I get these errors:
Rand.java:72: variable a might not have been initialized
a++ ;
^
Rand.java:74: variable b might not have been initialized
b++ ;
^
2 errors
It seems to me that I initialized them at the top of the method. What's going wrong?
You declared them, but you didn't initialize them. Initializing them is setting them equal to a value:
int a; // This is a declaration
a = 0; // This is an initialization
int b = 1; // This is a declaration and initialization
You get the error because you haven't initialized the variables, but you increment them (e.g., a++) in the for loop.
Java primitives have default values but as one user commented below
Their default value is zero when declared as class members. Local variables don't have default values
Local variables do not get default values. Their initial values are undefined with out assigning values by some means. Before you can use local variables they must be initialized.
There is a big difference when you declare a variable at class level (as a member ie. as a field) and at method level.
If you declare a field at class level they get default values according to their type. If you declare a variable at method level or as a block (means anycode inside {}) do not get any values and remain undefined until somehow they get some starting values ie some values assigned to them.
If they were declared as fields of the class then they would be really initialized with 0.
You're a bit confused because if you write:
class Clazz {
int a;
int b;
Clazz () {
super ();
b = 0;
}
public void printA () {
sout (a + b);
}
public static void main (String[] args) {
new Clazz ().printA ();
}
}
Then this code will print "0". It's because a special constructor will be called when you create new instance of Clazz. At first super () will be called, then field a will be initialized implicitly, and then line b = 0 will be executed.
You declared them, but not initialized.
int a; // declaration, unknown value
a = 0; // initialization
int a = 0; // declaration with initialization
You declared them, but you didn't initialize them with a value. Add something like this:
int a = 0;
You declared them but did not provide them with an intial value - thus, they're unintialized. Try something like:
public static Rand searchCount (int[] x)
{
int a = 0 ;
int b = 0 ;
and the warnings should go away.
Since no other answer has cited the Java language standard, I have decided to write an answer of my own:
In Java, local variables are not, by default, initialized with a certain value (unlike, for example, the field of classes). From the language specification one (§4.12.5) can read the following:
A local variable (§14.4, §14.14) must be explicitly given a value
before it is used, by either initialization (§14.4) or assignment
(§15.26), in a way that can be verified using the rules for definite
assignment (§16 (Definite Assignment)).
Therefore, since the variables a and b are not initialized :
for (int l= 0; l<x.length; l++)
{
if (x[l] == 0)
a++ ;
else if (x[l] == 1)
b++ ;
}
the operations a++; and b++; could not produce any meaningful results, anyway. So it is logical for the compiler to notify you about it:
Rand.java:72: variable a might not have been initialized
a++ ;
^
Rand.java:74: variable b might not have been initialized
b++ ;
^
However, one needs to understand that the fact that a++; and b++; could not produce any meaningful results has nothing to do with the reason why the compiler displays an error. But rather because it is explicitly set on the Java language specification that
A local variable (§14.4, §14.14) must be explicitly given a value (...)
To showcase the aforementioned point, let us change a bit your code to:
public static Rand searchCount (int[] x)
{
if(x == null || x.length == 0)
return null;
int a ;
int b ;
...
for (int l= 0; l<x.length; l++)
{
if(l == 0)
a = l;
if(l == 1)
b = l;
}
...
}
So even though the code above can be formally proven to be valid (i.e., the variables a and b will be always assigned with the value 0 and 1, respectively) it is not the compiler job to try to analyze your application's logic, and neither does the rules of local variable initialization rely on that. The compiler checks if the variables a and b are initialized according to the local variable initialization rules, and reacts accordingly (e.g., displaying a compilation error).
You declared them at the start of the method, but you never initialized them. Initializing would be setting them equal to a value, such as:
int a = 0;
int b = 0;
Imagine what happens if x[l] is neither 0 nor 1 in the loop. In that case a and b will never be assigned to and have an undefined value.
You must initialize them both with some value, for example 0.
It's a good practice to initialize the local variables inside the method block before using it. Here is a mistake that a beginner may commit.
public static void main(String[] args){
int a;
int[] arr = {1,2,3,4,5};
for(int i=0; i<arr.length; i++){
a = arr[i];
}
System.out.println(a);
}
You may expect the console will show '5' but instead the compiler will throw 'variable a might not be initialized' error. Though one may think variable a is 'initialized' inside the for loop, the compiler does not think in that way. What if arr.length is 0? The for loop will not be run at all. Hence, the compiler will give variable a might not have been initialized to point out the potential danger and require you to initialize the variable.
To prevent this kind of error, just initialize the variable when you declare it.
int a = 0;
You haven't initialised a and b, only declared them. There is a subtle difference.
int a = 0;
int b = 0;
At least this is for C++, I presume Java is the same concept.
Set variable "a" to some value like this,
a=0;
Declaring and initialzing are both different.
Good Luck

In what order are initializer block and variable definitions and etc. executed? (in java)

I have problem understanding the order in which initialization happens. this is the order I assumed:
*Once per
1. Static variable declaration
2. Static block
*Once per object
3. variable declaration
4. initialization block
5. constructor
but according to this code I am obviously wrong:
class SomethingWrongWithMe
{
{
b=0; //no. no error here.
int a = b; //Error: Cannot reference a field before it is defined.
}
int b = 0;
}
And the error would disappear if I do this:
class SomethingWrongWithMe
{
int b = 0;
{
b=0;
int a = b; //The error is gone.
}
}
I can't figure out why isn't there an error on
b=0;
The Java Language Specification (section 8.3.2.3) says you can use a variable on the left hand side of an expression, i.e. assign to it, before it is declared, but you cannot use it on the right hand side.
All variables are initialized to their default values, then explicit initializers and anonymous blocks are run in the order they are found in the source file. Finally the constructor is called.
Statics are only run once on the first use of a class.
The compile error appears to be a rule of Java rather than something that necessarily makes sense in every case.
Variable definitions are not done "before" blocks. They are both done at the same time, in the order that they are defined
class SomethingWrongWithMe {
{
b = debug("block 1");
}
int b = debug("define");
{
b = debug("block 2");
}
private int debug(String str) {
System.out.println(str);
return 0;
}
}
Output
block 1
define
block 2
First of all, your assumptions are more or less correct, except for the fact that declarations (with initialization, such as int b = 0) and instance initializer blocks are executed in the order they are written.
int b = 0; // executed first
{
b = 1; // executed second
}
int a = b; // executed third
Also note that the declaration i.e. int b is not executed. The declaration just declares the existence of the variable.
As for the error you got (or, rather the error you didn't get) I agree that it looks strange. I assume that the compiler deals with referencing a variable in an expression and assigning a value to it in different ways. When writing to a variable in an instance initializer, it just checks that the variable is there, while when reading from it, it requires it to be declared above the instance initializer block. I'll see if I can find a reference for that in the JLS.

What is the scope of a Java variable in a block? [duplicate]

This question already has answers here:
Why does Java not have block-scoped variable declarations?
(6 answers)
Closed 5 years ago.
I know in C++ variables have block scope, for example, the following code works in C++:
void foo(){
int a = 0;
for (int i = 0; i < 10; ++i){
int a = 1; // Redefine a here.
}
}
But this snippet doesn't work in Java, it reports "duplicate local variable a", does it mean Java variables don't have BLOCK scope?
They have block scope. That means that you can't use them outside of the block. However Java disallows hiding a name in the outer block by a name in the inner one.
java variables do have a block scope but if you notice int a is already defined in scope
{
int a = 0;
{
{
}
}
}
all subscopes are in scope of the uppermost curly braces. Hence you get a duplicate variable error.
Section §14.4.2 says:
The scope of a local variable
declaration in a block (§14.2) is the
rest of the block in which the
declaration appears, starting with its
own initializer (§14.4) and including
any further declarators to the right
in the local variable declaration
statement.
The name of a local variable v may not
be redeclared as a local variable of
the directly enclosing method,
constructor or initializer block
within the scope of v, or a
compile-time error occurs.
The previous answers already stated the reason, but I just want to show that this is still allowed:
void foo(){
for(int i = 0; i < 10; ++i){
int a = 1;
}
int a = 0;
}
In this case the a inside the loop doesn't hide the outer a, so it's valid.
Also IMHO it should be this way in C++ too, it's less confusing and prevents accidental declaration of variable with same name.
It does, but it's nested, so the "a" you defined in foo() is available in all blocks within foo.
Here is an example of what you're looking for:
void foo(){
{
int a = 0;
// Do something with a
}
for(int i = 0; i < 10; ++i){
int a = 1; //define a here.
}
}

Categories