What is happening in the compiler when we do forward referencing methods, how does it assign the value of another variable which is not declared? And can we use that inside methods? But why can we not use the variable in static blocks? For example,
public class Forward {
static int i = test();
static int test() {
System.out.println(j);
j = 20;
return j;
}
static {
System.out.println(j);
j = 20;
}
static int j;
}
If we assign the value directly like:
int i = j;
int j = 10;
Why does this code not compile? How is it possible only with methods? How does the compiler compile the forward references internally? Is the declaration happening first for all the variables and initialization happening next for all at a time or one by one? Explain it in detail.
JLS Section 8.3.3 states that 4 criteria must all be met if the forward-usage of a static variable will be considered a compile-time error:
The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
In other words, obviously, it must be a "forward declaration": you use it before you declare it:
// Maybe not OK to use j here.
static int j;
// OK to use j here.
The use is a simple name in either a class variable initializer of C or a static initializer of C;
You can make a forward reference to the variable if you qualify its name:
static {
System.out.println(j); // Error on this line...
System.out.println(Forward.j); // ...but not this line...
System.out.println(org.whatever.Forward.j); // ...or this line...
}
static int j;
This criterion only applies to variable or static initializers. This answers the specific question of why you are not getting a compiler error when referring to the static variable in a method.
However, let's just finish things off...
The use is not on the left hand side of an assignment;
Note in this code:
static {
System.out.println(j); // Error on this line...
j = 20; // ...but not this line.
}
static int j;
You can't do anything except assign the variable, even if you reverse the order of the lines (you can't assign and then print it).
C is the innermost class or interface enclosing the use.
The following code would be fine:
class Forward {
static class Inner {
static {
System.out.println(j);
}
}
static int j;
}
Related
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
}
JLS 8.1.3 gives us the rule about variables which are not declared in an inner class but used in the class.
Any local variable, formal parameter, or exception parameter used but
not declared in an inner class must either be declared final or be
effectively final (§4.12.4), or a compile-time error occurs where the
use is attempted.
An example:
class A{
void baz(){
int i = 0;
class Bar{ int j = i; }
}
public static void main(String[] args){
}
}
DEMO
Why was the code compiled? We used the non-final local variable in the inner class which was no declared in there.
Variable i defined inside method baz is effictively final because value of variable i is not modified elsewhere. If you change it
void baz(){
int i = 0;
i = 2;
class Bar{ int j = i; }
}
The code will fail to compile because variable i is no longer effictively final but if you just declare the variable i and the initialize it in another line, the code will compile because the variable is effictively final
void baz(){
int i;
i = 2;
class Bar{ int j = i; }
}
i is effectively final, since it is never modified. As you yourself quoted the JLS, the inner class may use effectively final variables.
Because i is effectively final as it is not changed in baz.
If I have:
class A
{
void foo()
{
int a = count;
}
void bar()
{
int a = c; // here ERROR
int c = 10;
}
private int count = 10;
}
here in foo count is used without problem also if it's declared after the use.
The same is not true in method bar where the variable c must be declared before its
use.
Which are the class scope rules? How they differs from method scope rules?
P.S.
My doubt is because the common scope resolutions rules:
When the compiler find count it should try to find it "back" to its
use but back there is Class A... so maybe private int count is "hoisting" at
beginning of Class A?
Imagine that you were a compiler, and you get to the line:
int a = c;
Won't you get angry and ask yourself "What is c"? The order is important1.
1count doesn't make a problem because it's a class member, it's known in the whole class. You can place class members in the beginning of the class, or at the end.
count is declared in the class while c is declared in the method
While compiling, the class would find count in the class.
class A
{
void foo()
{
int a = count;
}
private int count = 10;
}
is equal to put count anywhere in the class, as it is a class member and can be found in the class everywhere.
while in your case of
class A
{
void bar()
{
int a = c; // here ERROR
int c = 10;
}
}
c is not defined before use, as c is a local variable in a method.
My question is regarding the declaration and value assignment rules in Java.
When writing the fields we can declare and assign values together but we cannot do the same separately.
E.G.:
class TestClass1 {
private int a = 1;
private int b ;
b= 1;
private int sum;
public int getA() {
return a;
}
public int getB() {
return b;
}
public int getSum() {
sum = a + b;
return sum;
}
}
public class TestClass {
public static void main(String[] args) {
TestClass1 testClass1 = new TestClass1();
System.out.println("total =" + testClass1.getSum());
}
}
Here in line:
private int a = 1;
We are able to declare a as a private int and assign a value 1 to it. But in case of:
private int b ;
b= 1;
Eclipse does not allow this to happen and throws an error. Kindly explain the logic behind this.
Code inside a class, but outside a function, is purely declarations. It does not get "executed". It simply declares what fields a class contains.
The reason you can do the shorthand private int a = 1; is just syntactic sugar that the Java language allows. In reality, what happens is that the a = 1 part is executed as part of the constructor. It's just easier to read and write when it is next to the variable declaration.
It's something nice that the Java langage creators allowed. Not every language allows that, look at C++ as an example that does not always allow it.
you have to put b=1; inside a method or put this inside a constructor.
you are getting this error since you can't do any thing other than declaration(private int a= 1;
) in class level.
It's just a question of syntax in Java. In the example you show, you try to affect a value to b in the member declaration part of your class. This is not allowed by the syntax of Java. You can only do it when you declare your attribute, in the body of a method or in a static block if your attribute is static e.g. :
private static int b;
static {
b = 1;
}
It is only a syntax problem. You can do it like this :
private int b ;
{ // <- Initialization block
b= 1;
}
See What is an initialization block?
You are unable to write logic directly in the class. You should move it to the constructor.
Java only allow declaration within the class and outside any method. Declarations like int b = 1; will be executed when you initialize a new Object.
Following code is going in else statement. I am not able to find out where i made mistake.
*A want to execute in below comments.
*B is executing in below comments.
package com.java;
import java.util.Scanner;
public class Solution
{
static int n;
static String w[];
public static void main(String[] args)
{
System.out.println("enter no of string between 1 to 50");
Scanner scanner = new Scanner(System.in);
//* A
if ((1<n) && (n<=50))
{
n = scanner.nextInt();
System.out.println("enter " +n+ "strings between 1 to 2000 length");
for (int i=0; i<n; i++)
{
w[i]= scanner.next();
if ((1<w[i].length()) && (w[i].length()<2000))
{
System.out.println("ok");
}
}
System.out.println(w);
}
// *B
else
{
System.out.println("coming due to static");
}
}
}
static means that it is a class variable, that is, it does not belong to an instance of the class. And opposite, a non static variable belongs to an instance of the class. You're referencing the variable n from a static method, and hence, it will not work unless the variable also is declared static.
(and obviously, the if itself won't work because of what the reply from #MarounMaroun mention)
You didn't initialize n, so you're not satisfying the if condition, since uninitialized static int variables are 0 by default.
So:
if ((1<n) && (n<=50)) is not evaluated to true, so else will be executed.
Note that you can't access static variable from non-static method (See #NilsH answer). And that's make a lot of sense..
First, when working with static methods, you must reference static variables. If you try to reference a non-static variable that belongs to a class, the compiler will complain because that is wrong. Static variables do not belong to a class per se.
Second, I think you have a typo or forgot some code. n is never set - ever. Therefore, since in the static context it will be zero initialized and hit the else. I think you meant for n to actually be set before the if statement either via user-input or some other means. If you leave everything static and actually provide a value for n, then your code should work.
For instance, you probably need to make this assignment:
n = scanner.nextInt();
before the if-statement.
there is another problem with your code in reading the next number you want to read, but I will leave that for you to solve.
Have you tried making static int n and static String w[] public?
IE:
public static int n ;
public static String w[] ;
what you probably want is moving all that code to a non-static method. and then in your main method just do something like this
Solution s = new Solution();
s.myNonStaticMethod();