-1

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.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • @OHGODSPIDERS wrong, as compiler has no way to understand which one would I want to use when I refer to `String` in my `main` method. – Giorgi Tsiklauri Dec 01 '22 at 13:22
  • The relevant rules are in [section 6.3 of JLS 17](https://docs.oracle.com/javase/specs/jls/se17/html/jls-6.html#jls-6.3) and 6.4 – Mark Rotteveel Dec 01 '22 at 13:22
  • @GiorgiTsiklauri obviously not wrong, since your code works and there is no conflict. As if there was a rule for that... – f1sh Dec 01 '22 at 13:24
  • @f1sh there must be a rule for that. I repeat my question: how, based on what, compiler implies which exact type I want to use if both are in scope then? – Giorgi Tsiklauri Dec 01 '22 at 13:26
  • @OHGODSPIDERS please take a look at the question carefully. I have never used a Fully Qualified Name.. and that is what brings the confusion. Also, you say "*...the compiler tries to resolve it according to his own rules*" - **which rules** in particular? – Giorgi Tsiklauri Dec 01 '22 at 13:29
  • @MarkRotteveel could you specify which subpoint/article is it? I can't find it. – Giorgi Tsiklauri Dec 01 '22 at 13:30
  • 1
    @MarkRotteveel the only seemingly relevant clause I found is this: "*A declaration d of a type named n shadows the declarations of any other types named n that are in scope at the point where d occurs throughout the scope of d.*" - yet, I really really find this definition ugly and confusing. – Giorgi Tsiklauri Dec 01 '22 at 13:37
  • 3
    The specific rule is "_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_". That means if you have a class named `String` in your package (`org.something.a`) every reference to the simple name `String` references your class `org.something.a.String` and not `java.lang.String`. – Thomas Kläger Dec 01 '22 at 13:38
  • In my case it does not compile saying that I need a myPackage.String but java.lang.String was provided. – Antoniossss Dec 01 '22 at 13:38
  • @ThomasKläger what? no way, Thomas. That is nowhere close to what my question is all about. What you're pointing out is just a definition of what the scope is. – Giorgi Tsiklauri Dec 01 '22 at 13:41
  • 2
    I think that Thomas is correct and members of your package have a precedence of members from different packages. Therfore String means myPackage.String instead of always included by default java.lang.String member – Antoniossss Dec 01 '22 at 13:44
  • @Antoniossss but it has certainly not been working like that! certainly! for years.. this would have bring a name clash compile-time error. What Thomas has pointed out, really doesn't read as *custom types have preferences*.. really. – Giorgi Tsiklauri Dec 01 '22 at 13:46
  • @GiorgiTsiklauri I was just about to post the same quote. Yes, that is imho basically a very technical thing of saying that a simple name resolved through the current package takes precedence over others like java standard packages like java.lang. If I had a good understandable definition of how names are resolved available I would have posted those as an answer. ;-) I think I misunderstand your original comment "This must be a name clash" as meaning you expected not to be able to declare any other class named String at all and already have the compiler complain there. – OH GOD SPIDERS Dec 01 '22 at 13:46
  • @GiorgiTsiklauri they have. And Thomas provided the correct excerpt from the JLS that specifies the behavior. And it explains exactly what the compiler is doing in your example and I can reproduce that locally. And in return you say "what? no way, Thomas". – f1sh Dec 01 '22 at 13:47
  • @f1sh are you aware, that `java.lang` is implicitly available everywhere in Java code? – Giorgi Tsiklauri Dec 01 '22 at 13:49
  • "custom types have preferences" <- But this isn't about custom types or not but about packages. You can have a custom type String in a different package `org.something.b` and it will not have any preference in `org.something.a`. – OH GOD SPIDERS Dec 01 '22 at 13:49
  • @OHGODSPIDERS and why then the `org.something.a.String` has a preference over `java.lang`? the behaviour of this code is - custom type has a preference over `java.lang`. Please have a look at the question, I've included clause about implicit `java.lang` import. – Giorgi Tsiklauri Dec 01 '22 at 13:54
  • @GiorgiTsiklauri yes, I am. But obviously the compiler first takes into account all classes in the scope of the class that you want to compile, as Thomas already commented. – f1sh Dec 01 '22 at 14:04
  • 3
    I added an answer to explain **why** the imports from `java.lang` **never** have precedence. It is because the package `java.lang` is imported as so-called "import-on-demand" which is only used if a class name cannot be locally resolved (within the current package or through a single-type-import). – Thomas Kläger Dec 01 '22 at 14:05
  • @ThomasKläger thank you, Thomas. I will examine it carefully now and will follow up with feedback. – Giorgi Tsiklauri Dec 01 '22 at 14:07

1 Answers1

5

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.

Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • Please have a look at the updated question. I've added some details.. also, if `java.lang.String` *never shadows*, then where does it go.. it's still unclear. – Giorgi Tsiklauri Dec 01 '22 at 14:31