is this a java compiler bug? - java

Compiler gives a compilation error that does not make sense.
"Cannot make a static reference to the non-static field x"
I do not make a static reference. A static inner class should have access to the private members of the enclosing class. In fact it does allow me to access super.x
I tried this with java 1.8
class Bug
{
private int x = 0;
int y;
static class BugDerived extends Bug
{
BugDerived()
{
super();
super.y = 1; // no error
y = 1; // no error
super.x = 1; // no error !
x = 1; // ERROR
}
}
}

No, a static method can only reference static fields or other methods. By calling super().x you reference a non-static property from a non-static context, which is allowed.
The following quote is taken from Oracle website.
Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.
The inner static class do not have access to members (private methods/variables) of the enclosing class.
Also look here:
a static nested class cannot refer directly to instance variables or
methods defined in its enclosing class: it can use them only through
an object reference. They are accessed using the enclosing class name.

A static inner class is linked to the outer class at class level, not instance level.
As a result, it can only access static members of the outer class, working in that respect identical to static methods.
Hence, this is not a compiler bug, it's expected behaviour.
When you use super.x, you're accessing the x data member from the instance of the superclass that underlies the instance of the nested class for which the constructor is being run, thus it does have access.

Related

Difference between class variable initializers , static initializers of the class, and the field initializers

i am trying to follow the JVM specs http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2.
Is frustrating for me to read the unclear specs. So:
What are the differences between:
class variable initializers
static initializers of the class
field initializers
/*
Initalizers
*/
class A {
private static String x = "sfi"; //static field initializer
private String ifi = "ifi"; //instance field initializer
static { //static (block) initalizer ?!
String y = "sbi";
}
{ //instance (block) initalizer ?!
String z = "ibi";
}
}
Static members
First you need to understand the difference between static and non-static fields of a class. A static field is not bound to any instance of the class, it is a property of the class (that is the reason why we access them through the class) so in your example you can access x from whithin A like this: A.x. A common example for this is counting the number of objects a class has:
private static int counter = 0;
public A()
{
counter++;
}
// get the instance count
public static int getCounter()
{
return counter;
}
This method we would call from somewhere else like this: A.getCounter() and we will retrieve the number of objects that have the type A.
Non-static members
These are variables that are specific to each object (instance) of the class. In your example this is sfi. The runtime system guarantees that sfi will be available whenever an object of type A is created and that it will have a default value of ifi, but the difference here is that each object you create will have a member called sfi with a default value of ifi so each object can later modify it of course.
Initializing blocks
They are a feature designed to be used when initialization cannot be done inline (initialization requires more complex logic like a for loop or error-checking). Here again we have:
static { /* init code ... /* }
which is a static initialization block that "can appear anywhere in the class body. The runtime system guarantees that static initialization blocks are called in the order that they appear in the source code" - from here
If, on the other hand, we want to initialize instance members but we cannot do it in one line then we can use a block but without the static keyword:
{
// init code for instance members
}
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

field cannot be declared static in a non-static inner type unless initialized with a constant expression

public class Test {
public enum Directions {
NORTH, WEST, SOUTH, EAST
}
static final Directions D1 = Directions.NORTH;
static class Inner {
static final Directions D2 = Directions.NORTH;
}
class Inner2 {
static final Directions D3 = Directions.NORTH;
}
}
I am getting the IDE-Error which is in the title, referring to the variable D3. Can someone explain that to me? Why can I not declare a static variable in an inner class that is not static and why is the enum value not a constant?
JLS §8.1.3 Inner Classes and Enclosing Instances
Inner classes may not declare static members, unless they are constant
variables (§4.12.4), or a compile-time error occurs.
Why is an Enum entry not considered a constant variable?
A variable of primitive type or type String, that is final and
initialized with a compile-time constant expression (§15.28), is
called a constant variable.
Static implies that it can be used without any instance. For instantiating Objects of non static inner class an instance of outer class is needed. Without an object of outer class non static nested inner class can not be instantiated.
class Inner2 {
static final Directions D3 = Directions.NORTH;
}
Inner2 is not static. Inner2 can not be used until its instantiated. Hence any references or methods can only be used once it is instantiated. As Inner2 is not static so existence of D3 only makes sense once we have an object of Inner2 and it being declared as static makes no sense.
For the second question I have another related doubt, so I prefer to add the link to the question I have asked : Why compile time constants are allowed to be made static in non static inner classes?
Hope fully once we have answer to that question, we will have better complete understanding.
Either move the class
class Inner2 {
static final Directions D3 = Directions.NORTH;
}
outside of the enumerator Or declare it static too
As per the java documentation:
Inner classes may not declare static initializers (§8.7) or member interfaces, or a compile-time error occurs.
Inner classes may not declare static members, unless they are constant variables (§4.12.4), or a compile-time error occurs.

Do (Instances of) Static Nested Classes Have Access to Outer Static Variables?

The following yields an error:
class A {
static int apple = 5;
static class StaticNestedSubClassOfA {
//...
}
}
class Test {
public static void main(String str[]) {
A.StaticNestedSubClassOfA b = new A.StaticNestedSubClassOfA();
System.out.println("Apple: " + b.apple);
}
}
ERROR:
Test.java:14: error: cannot find symbol
System.out.println("Apple: " + b.apple);
^
symbol: variable apple
location: variable b of type StaticNestedSubClassOfA
1 error
But doesn't StaticNestedSubClassOfA have access to the static variable apple? (I suppose this doesn't mean that objects spawned from StaticNestedSubClassOfA have access to apple through their instances?)
You are accessing an A.apple in the class Test This has no chance of working.
When A is compiled it doesn't know how it will be accessed from all possible classes and cannot generate the access methods required to make this work.
The JVM doesn't allow access to private members between classes. Instead the javac compiler adds methods for accesses in the same java class file under the outer class. This is why you see methods like access$200 in the call stack some times. An class accessed a private member from another class via a generated accessor method. If you are in an unrelated class, you can't access private fields, nor could the javac implement a means for you to do so without creating all possible accessors in advance (which it doesn't do)
This is the only way I could think of to get this to work.
public class Outer
{
private static int apples = 5;
public static class Inner {
public int getApples() { return Outer.apples; }
}
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
System.out.println( inner.getApples() );
}
}
As far as the run time is concerned, Outer and Inner are two separate classes. The idea that they are nested is a fiction maintained by the compiler. Thus at run time there is really no way for the compiler to access Outer by using Inner.
Because they share a scope, however, the compiler does allow Inner to access private methods and fields in Outer. But that's a trick the compiler can pull off without the run time's help. Other tricks, like accessing the field directly by name as you are trying to do, aren't possible.

Why can't I refer enclosing class's non-static var from its inner class's non-static method?

In my awareness, non-static method will be assigned "this" variables for its class obj & all enclosing classes.
public class TestNested {
int a=4;
public static class a{
int a=5;
static int c=10;
class b{
int a=6;
void aaa(){
int a=7;
TestNested t=new TestNested();
System.out.println(this.a);
System.out.println(b.this.a);
System.out.println(TestNested.a.b.this.a);
System.out.println(TestNested.a.this.a);
System.out.println(a.this.a);
System.out.println(t.a);
System.out.println(TestNested.this.a);
}
}
}
void r(){
TestNested t=new TestNested();
TestNested.a a=new TestNested.a();
a.b b=a.new b();
b.aaa();
}
public static void main(String[] args) {
TestNested t=new TestNested();
t.r();
}
}
in this case the final statement of void aaa() is System.out.println(TestNested.this.a); will be sentenced to cause compilation error with the reason :'com.xxx.TestNested.this' cannot be referenced from static context, that's really confusing me because the this var that points to TestNested should be the non-static hidden var of the method itself,then why it can't use its own var?
Or if my awareness is wrong that each that var is assigned in each class enclosed from the method's class, but the void aaa() isn't a static method which means it can reference non-static var rite? or even the method isn't static, but if one of its enclosing class is static, it'll be automatically recognized as static member?
This is because your nested class a is not an inner class of TestNested. It is a static nested class, meaning that it is not linked to a particular instance of TestNested.
Note: an inner class is a non-static nested class.
What instance of TestNested would you expect your expression TestNested.this to refer to?
You can see, by the way, that you're not referring to your variable t here:
TestNested.a a=new TestNested.a();
Which points out that the object a is not linked to t at all.
In my above answer, I assumed it was clear to you what you were doing with this. It appears it is not the case, according to your comments, so I'm going to try and clarify it here.
First of all, this always refers to an object: an instance of a class.
Let's assume we're in the context of a non-static method in the class b. Because the method is non-static, the code will be executed relatively to a particular instance of b. I take the shortcut to refer to this particular instance as "the instance of b you're in".
Since b is an inner class of a, no instance of b can exist outside an instance of a. This means the instance of b you're in is enclosed in an instance of a. I take the shortcut to refer to this particular instance as "the instance of a you're in" (Technically, you're in a b which is in an a).
So, in this context of a non-static method of b:
this refers to the instance of the b you're in. It is the standard use of this keyword.
b.this, a.b.this or TestNested.a.b.this are the same as this here, the difference is only that you qualify more precisely the class b.
a.this or TestNested.a.this both refer to the instance of a you're in (TestNested.a is just a more precise qualification for a). This a object exists because b is an inner class of a, which means that every instance of b is linked to an instance of a. This is the only way to refer to the instance of a.
TestNested.this would refer to the instance of TestNested you're in. But you're not in any instance of TestNested, so it does not mean anything, hence the compile error. You're in an instance of b, which is within an instance of a (because b is an inner class of a). The instance of a exists by itself because a is a static nested class of TestNested, so it is not linked to an instance of TestNested.
TestNested.a <-- references a static variable of a static class
this.a <-- references an instance variable of an object
TestNested.this <-- tries to reference an object from a static context, but "this" does not exist in a static context.
You can reference static content from non-static, but not vice-versa.

Accessibility of members of top level class in inner class?

I have a query regarding accessibility of top level class from member inner class.
I have just read the reason why local or anonymous inner classes can access only final variables.The reason being JVM handles these two classes as entirely different classes and so, if value of variable in one class changes, it can't be reflected at run time in another class file.
Then, my question is that how an inner member class (non-static) can have access to members to members of top level class, as JVM is still treating these two classes as different class files? If value of a member variable of top level class changes, how will it possible to reflect in class file of inner class at runtime?
They're separate classes, but there's an implicit reference to the instance of the "outer" class in the "inner" class. It basically acts as a variable which you can get at either implicitly or via special syntax of ContainingClassname.this.
Note that if you don't want such an implicit reference, you should declare the nested class as static:
public class Outer
{
private class Inner
{
// There's an implicit reference to an instance of Outer in here.
// For example:
// Outer outer = Outer.this;
}
private static class Nested
{
// There's no implicit reference to an instance of Outer here.
}
}
this is implicitly final, you cannot change it. When you write some thing like
class Outer {
int a;
class Inner {
{ a = 1; }
}
}
you are actually writing the same as
class Outer {
int a;
class Inner {
{ Outer.this.a = 1; }
}
}
The a is not final but the Outer.this is, and that is the reference which is used.

Categories

Resources