Let's say I have a:
package org.something.a;
public class String {
...
}
and
package org.something.a;
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());
}
}
I wonder why there's no name clash for String
, as java.lang.String
and org.something.a.String
are both available in a org.something.a
package, on a compile-time class-path, and why custom-defined class (org.something.b
) has a preference, as IDE (IntelliJ) resolves String
to org.something.a.String
?
Besides, if I'll compile both files with javac *.java
and then invoke java Main
, I get this:
Error: Main method not found in class Main, please define the main method as: public static void main(String[] args)
but if I change the main method signature to:
public static void main(java.lang.String[] args) {...}
then it compiles and prints String
. Which means, it then compiles entire file having imported java.lang.String
and it failed before because, main
method didn't have a java.lang.String
array as a parameter.
I am well aware of this: JLS §7:
Code in a compilation unit automatically has access to all classes and interfaces declared in its package and also automatically imports all of the public classes and interfaces declared in the predefined package java.lang.
and as far as I remember, this must be a name clash. But even if it's not, the behaviour above is really unclear - why custom class has a preference and why everything changes if I just append java.lang
to the main method's parameter.
Cannot find any appropriate spec page in JLS.
6.3 Scope of a Declaration states:
Then why does
org.something.a.String
not clash withjava.lang.String
, sincejava.lang.*
is imported into every compilation unit?7.3 Compilation Units states:
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
And 6.4.1 Shadowing says about import-on-demand:
Therefore
java.lang.String
can never shadow a class namedString
(and similar for other classes fromjava.lang
)With the two classes
you cannot run
org.something.a.Main
because thatmain
method has a signature ofpublic static void main(org.something.a.String[] args)
, but to be able to run that class the signature must bepublic static void main(java.lang.String[] args)
. Note the most of the times you can just write the method aspublic static void main(String[] args)
because there is no classString
in the current package that shadowsjava.lang.String
.If you change your code to
then you will be able to run that class because now the signature of the main method is correct.
Running this code will output
To see the difference between
java.lang.String
andorg.something.a.String
you should expand yourmain
method towhich will output
How does the mapping of
String
tojava.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: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 namejava.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:For completeness: the reason why I wrote if you can find exactly one such class is this: nobody prevents you from writing
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 nameList
the compiler will abort with an error because with those two import statements it cannot know whether you want the class nameList
to refer tojava.awt.List
or tojava.util.List
.In your case (where the class
String
is in the same package as the classMain
) this reason doesn't apply. There is no implicitimport 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 nameString
that no longer meansjava.lang.String
but refers to the classString
from the current package.