6.3 Scope of a Declaration states:
The scope of a top level class or interface (§7.6) is all class and interface declarations in the package in which the top level class or interface is declared.
Then why does org.something.a.String
not clash with java.lang.String
, since java.lang.*
is imported into every compilation unit?
7.3 Compilation Units states:
Every compilation unit implicitly imports every public class or interface declared in the predefined package java.lang, as if the declaration import java.lang.*;
appeared at the beginning of each compilation unit immediately after any package declaration. As a result, the names of all those classes and interfaces are available as simple names in every compilation unit.
Note that it explicitly states the import statement as import java.lang.*;
, which is called an "import on demand":
7.5.2 Type-Import-on-Demand Declarations
A type-import-on-demand declaration allows all accessible classes and interfaces of a named package, class, or interface to be imported as needed.
TypeImportOnDemandDeclaration:
import PackageOrTypeName . * ;
And 6.4.1 Shadowing says about import-on-demand:
A type-import-on-demand declaration never causes any other declaration to be shadowed.
Therefore java.lang.String
can never shadow a class named String
(and similar for other classes from java.lang
)
With the two classes
package org.something.a;
class String {}
public class Main {
public static void main(String[] args) {
String a = new String(); //IDE shows that this is a org.something.a.String type and not java.lang.String
System.out.println(a.getClass().getName());
}
}
you cannot run org.something.a.Main
because that main
method has a signature of public static void main(org.something.a.String[] args)
, but to be able to run that class the signature must be public static void main(java.lang.String[] args)
. Note the most of the times you can just write the method as public static void main(String[] args)
because there is no class String
in the current package that shadows java.lang.String
.
If you change your code to
package org.something.a;
class String {}
public class Main {
public static void main(java.lang.String[] args) {
String a = new String(); //IDE shows that this is a org.something.a.String type and not java.lang.String
System.out.println(a.getClass().getName());
}
}
then you will be able to run that class because now the signature of the main method is correct.
Running this code will output
org.something.a.String
To see the difference between java.lang.String
and org.something.a.String
you should expand your main
method to
package org.something.a;
class String {}
public class Main {
public static void main(java.lang.String[] args) {
String a = new String(); //IDE shows that this is a org.something.a.String type and not java.lang.String
System.out.println("a has class " + a.getClass().getName());
System.out.println("args has class " + args.getClass().getName());
System.out.println("args has component type " + args.getClass().componentType().getName());
java.lang.String b = new java.lang.String();
System.out.println("b has class " + b.getClass().getName());
}
}
which will output
a has class org.something.a.String
args has class [Ljava.lang.String;
args has component type java.lang.String
b has class java.lang.String
How does the mapping of String
to java.lang.String
disappear?
It doesn't disappear, it doesn't manifest itself in the first place.
It is important to understand that all those import statements are a mere convenience feature for us lazy programmers.
A single-type-import statement of the form import java.util.List;
lets us tell the compiler:
Look, anytime I write List
as a simple, unqualified class name I want you to use java.util.List
as the qualified class name.
This import statement doesn't prevent you from using the class java.awt.List
- it just means that you always have to use the fully qualified class name java.awt.List
if you want to use that specific class. And it doesn't change a bit for the compilation process - the compilation process internally always only ever uses the fully qualified class names.
An import-on-demand statement of the form import java.lang.*;
which is implicitly the first import statement of every compilation unit is even more lazy. It tells the compiler:
Look, anytime I write String
as a simple, unqualified class name and you cannot find the definition of the String
class neither in the current package nor in a single-type-import statement, look in all those packages that are imported on demand whether you can find such a class in any of those packages. And if you can find exactly one such class there use that's class fully qualified class name.
For completeness: the reason why I wrote if you can find exactly one such class is this: nobody prevents you from writing
import java.awt.*;
import java.util.*;
And the compiler will still happily compile your code - until the moment where you try to use the simple class name List
. As soon as you try to use both import statements and the simple class name List
the compiler will abort with an error because with those two import statements it cannot know whether you want the class name List
to refer to java.awt.List
or to java.util.List
.
In your case (where the class String
is in the same package as the class Main
) this reason doesn't apply. There is no implicit import org.something.a.*
.
** if java.lang.String
never shadows, then where does it go..**
java.lang.String
is always there. It is only the simple name String
that no longer means java.lang.String
but refers to the class String
from the current package.