Why does Java not have block-scoped variable declarations? - java

The following method does not work because the inner block declares a variable of the same name as one in the outer block. Apparently variables belong to the method or class in which they are declared, not to the block in which they are declared, so I therefore can't write a short little temporary block for debugging that happens to push a variable in the outer scope off into shadow just for a moment:
void methodName() {
int i = 7;
for (int j = 0; j < 10; j++) {
int i = j * 2;
}
}
Almost every block-scoped language I've ever used supported this, including trivial little languages that I wrote interpreters and compilers for in school. Perl can do this, as can Scheme, and even C. Even PL/SQL supports this!
What's the rationale for this design decision for Java?
Edit: as somebody pointed out, Java does have block-scoping. What's the name for the concept I'm asking about? I wish I could remember more from those language-design classes. :)

Well, strictly speaking, Java does have block-scoped variable declarations; so this is an error:
void methodName() {
for (int j = 0; j < 10; j++) {
int i = j * 2;
}
System.out.println(i); // error
}
Because 'i' doesn't exist outside the for block.
The problem is that Java doesn't allow you to create a variable with the same name of another variable that was declared in an outer block of the same method. As other people have said, supposedly this was done to prevent bugs that are hard to identify.

Because it's not uncommon for writers to do this intentionally and then totally screw it up by forgetting that there are now two variables with the same name. They change the inner variable name, but leave code that uses the variable, which now unintentially uses the previously-shadowed variable. This results in a program that still compiles, but executes buggily.
Similarly, it's not uncommon to accidentally shadow variables and change the program's behavior. Unknowingly shadowing an existing variable can change the program as easily as unshadowing a variable as I mentioned above.
There's so little benefit to allowing this shadowing that they ruled it out as too dangerous. Seriously, just call your new variable something else and the problem goes away.

I believe the rationale is that most of the time, that isn't intentional, it is a programming or logic flaw.
in an example as trivial as yours, its obvious, but in a large block of code, accidentally redeclaring a variable may not be obvious.
ETA: it might also be related to exception handling in java. i thought part of this question was discussed in a question related to why variables declared in a try section were not available in the catch/finally scopes.

It leads to bugs that are hard to spot, I guess. It's similar in C#.
Pascal does not support this, since you have to declare variables above the function body.

The underlying assumption in this question is wrong.
Java does have block-level scope. But it also has a hierarchy of scope, which is why you can reference i within the for loop, but not j outside of the for loop.
public void methodName() {
int i = 7;
for (int j = 0; j < 10; j++) {
i = j * 2;
}
//this would cause a compilation error!
j++;
}
I can't for the life of me figure out why you would want scoping to behave any other way. It'd be impossible to determine which i you were referring to inside the for loop, and I'd bet chances are 99.999% of the time you want to refer to the i inside the method.

another reason: if this kind of variable declaration were allowed, people would want (need?) a way to access outer block variables. may be something like "outer" keyword would be added:
void methodName() {
int i = 7;
for (int j = 0; j < 10; j++) {
int i = outer.i * 2;
if(i > 10) {
int i = outer.outer.i * 2 + outer.i;
}
}
}

Related

Variable Declared Outside of For Loop Vs Inside

//assume there is a java class call Node, and node is an array of Node
for(Node i: node){
Node j = i;
}
Node j;
for(Node i: node){
j = i;
}
Can someone please explain essentially what's the difference between this two?
Node j; on its own isn't 'code', in the sense that it results in zero bytecode - it does nothing on its own. It's a 'declaration'. It's an instruction to the compiler.
You're telling the compiler: There is this thing; I shall call it j, and it is a local variable. Please fail my compilation unless you can 100% ensure that this variable only ever holds either null or " reference to an object whose actual type is either Node or some subtype of Node". It decline to assign something to this variable right now; please fail to compile if I attempt to access it before you can 100% guarantee that some code has already ran that definitely assigned it first.
That's a ton of instructions for the compiler and absolutely none of it requires any code right then and there - it's just instructions for what to do in other lines of code - for example, in your second snippet, without that Node j; at the top, j = i; is not something that the compiler can do for you. It has no idea what you intend to do there; it does not know what j is, you did not inform the compiler about it, and in java, the language spec indicates that you can't just make up variable names without first telling the compiler about them.
Thus, bytecode wise, the exact difference between your 2 specific snippets here is nothing - local variables are a figment of javac's imagination: As a concept, 'local variable' doesn't exist, at all, at the JVM/bytecode level: It's just 'slots' in the local frame (i.e. local variables have a number, not a name), or on-stack space.
In other words, to figure out what the difference might be, it's entirely a matter of answering the question: How does javac interpret these 2 snippets? Because bytecode wise there is not going to be any difference, ever. Node j; simply cannot be bytecode - there is no such thing as a 'local variable' there, after all.
The only crucial difference is that any given local variable declaration (such as Node j;) declares its existence for a certain scope: It says: "There is this thing called j for these specific lines and not for any others". That scope is simply the next nearest pair of braces. So, in:
void foo() {
Node j;
for(Node i: node){
j = i;
}
}
You have declared that there is one local variable named j, and this applies to the entire foo() method (given that those are the nearest pair of braces when you look 'outwards' from your Node j; declaration.
Whereas with:
void foo() {
for(Node i: node){
Node j = i;
}
}
The 'scope' of your j declaration is that for loop - after all, that's the 'nearest pair'. In other words, this is almost legal:
void foo() {
Node j;
for(Node i: node){
j = i;
}
System.out.println(j);
}
Except for one tiny little detail: The compiler cannot 100% guarantee that j is actually assigned when we get to the println line - after all, what if your node collection is actually empty? Then j = i; ran zero times. So if you try to compile the above, the compiler will actually refuse, saying j is not definitely assigned. But make that Node j = null; and the above code would compile and run. In contrast to:
void foo() {
for(Node i: node){
Node j = i;
}
System.out.println(j);
}
This one doesn't compile with the error j? What are you talking about - the j you declared here only applies to all lines within the {} that goes with the for loop, thus, the mentioning of j in the println line is mentioning an unknown thing, and thus the compiler will complain about it in those terms: What is j?
So what do I do?
Follow conventions: When in rome, act like romans. When programming java, write it the way the vast majority of java coders would. Which means: Declare local variables at the latest possible point, at the right scope level. Thus, something like:
void foo() {
System.out.println("Starting method");
Node lastVisited = null;
for (Node i : nodes) {
lastVisited = i;
}
System.out.println("Last visited: " + lastVisited);
}
Don't declare lastVisited at the top of the method.
Do declare it before the for loop as we need it outside of it.
Names are important.

what does static signifies in object creation?

public static int f(String input) {
public static Stack<Character> stack = new Stack<Character>();
int n = 0;
for (int i = 0; i < input.length(); i++) {
if (input.charAt(i) == 'd')
stack.push('d');
else if (input.charAt(i) == 'b') {
if (!stack.empty() && stack.pop() == 'd') n++;
}
}
return n;
}
i just want to know the significance of static keyword in object creation...just pasted the whole code here
What everyone else has said so far is all accurate, but I'll elaborate a bit on your question on what "the significance of static keyword in object creation" is.
Static variables last for the lifetime of the class (so essentially from the start till the end of your program). But in your code, you declare your stack variable inside of a method, so that variable is created and destroyed each time that method is run. Therefore it doesn't actually make any sense to be given the keyword static. These variables are meant to be shared among every instance of the class you create. So you should only create that static variable once when your program is run.
(There are ton of resources elsewhere explaining when/where to use static)
Here are a couple I found useful when learning:
stackoverflow link to a similar, but more general discussion on the static keyword
A longer but good beginner introduction to all you need to know about the static keyword
As #azurefrog says, that won't work - I'm guessing you've copied and pasted the line
public static Stack<Character> stack = new Stack<Character>();
from the top of your class for illustrative purposes? If so, you've broken your example in doing so.
Assuming I'm correct and that declaration is from within your class, rather than within that static method, then the answer to your question would be that there will be only a single instance of the stack variable, regardless of the number of instances which you create of the class in which it resides: i.e. all instances which you create of your class will see/contain the same instance of stack, and anywhere it is addressed from outside (valid, given that it's public), should be referring to it as ClassName.stack, not instanceName.stack

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

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
}

How are local variables in method counted java bytecode

I have been learning about java byte-code recently, and i have been understanding most of it, but i am confused about how the local variable count for example is counted. I thought it would just be the total of the local variables, but this code generates 1 local variable when looking through the bytecode
public int testFail()
{
return 1;
}
But i thought it should be zero local variables because no local variable are defined.
Additionally this method also generates one local variable but it has more local variables than the previous example.
Finally this method
public static int testFail(int a, int b)
{
return a+b;
}
gnerates two local variable in the bytecode.
public static int testFail(int a)
{
return a;
}
Non-static methods use a local variable slot for this. Another complication is that longs and doubles count as 2 each. Also, depending on your compiler and settings, you may not see a one-to-one mapping between local variables in the source code and local variables in the byte code. For example, if debug information is left out, the compiler may eliminate unnecessary local variables.
Edit:
I just remembered: compilers may also re-use local variable slots. For example, given this code:
public static void test() {
for(int i = 0; i < 100; i++) {
...
}
for(int j = 0; j < 100; j++) {
}
}
the same slot can be used for i and j because their scopes don't overlap.
The reason the first one has a local variable is because it is a nonstatic method, so there is an implicit this parameter.

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