OK, here's a very curious Java 7 language puzzle for the JLS specialists out there. The following piece of code won't compile, neither with javac nor with Eclipse:
package com.example;
public class X {
public static X com = new X();
public void x() {
System.out.println(com.example.X.com);
// cannot find symbol ^^^^^^^
}
}
It appears as though the member com
completely prevents access to the com.*
packages from within X
. This isn't thoroughly applied, however. The following works, for instance:
public void x() {
System.out.println(com.example.X.class);
}
My question(s):
- How is this behaviour justified from the JLS?
- How can I work around this issue
Note, this is just a simplification for a real problem in generated code, where full qualification of com.example.X
is needed and the com
member cannot be renamed.
Update: I think it may actually be a similar problem like this one: Why can't I "static import" an "equals" method in Java?
This is called obscuring (jls-6.4.2).
A simple name may occur in contexts where it may potentially be
interpreted as the name of a variable, a type, or a package. In these
situations, the rules of §6.5 specify that a variable will be chosen
in preference to a type, and that a type will be chosen in preference
to a package. Thus, it is may sometimes be impossible to refer to a
visible type or package declaration via its simple name. We say that
such a declaration is obscured.
Your attribute com.example.X.com is not static so it can't be accessed via your X class in a static way. You can access it only via an instance of X.
More than that, each time you will instanciate an X, it will lead to a new X : I can predict a memory explosion here.
Very bad code :)
How can I work around this issue?
Using a fully qualified class name here can be a problem because, in general, package names and variable names both start with lower case letters and thus can collide. But, you do not need to use a fully qualified class name to gain a reference a class's static member; you can reference it qualified just by the class name. Since class names should start with an upper case character, they should never collide with a package name or variable. (And you can import an arbitrary class with its fully qualified class name without issue, because the import statement will never confuse a variable name for a package name.)
public void x() {
// System.out.println(com.example.X.com);
// cannot find symbol ^^^^^^^
System.out.println(X.com); // Works fine
}