繁体   English   中英

当两个具有相同名称的类可用时,为什么我没有名称冲突?

[英]Why I don't have a clashing name conflict when two classes with same names are available?

假设我有一个:

package org.something.a;

public class String {
   ...
}

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());
    }
}

我想知道为什么String没有名称冲突,因为java.lang.Stringorg.something.a.String在编译时类路径上的org.something.a package 中都可用,以及为什么自定义class ( org.something.b ) 有偏好,因为 IDE (IntelliJ) 将String解析为org.something.a.String

此外,如果我用javac *.java编译这两个文件,然后调用java Main ,我得到这个:

报错:Main method not found in class Main,请将main方法定义为:public static void main(String[] args)

但是如果我将主要方法签名更改为:

public static void main(java.lang.String[] args) {...}

然后它编译并打印String 这意味着,它然后编译导入java.lang.String的整个文件,之前它失败了,因为main方法没有java.lang.String数组作为参数。

我很清楚这一点: JLS §7

编译单元中的代码自动访问其 package 中声明的所有类和接口,并自动导入预定义 package java.lang 中声明的所有公共类和接口。

据我所知,这一定是名字冲突。 但即使不是,上面的行为也确实不清楚 - 为什么自定义 class 有偏好,为什么如果我只是 append java.lang到主要方法的参数,一切都会改变。

在 JLS 中找不到任何合适的规范页面。

6.3 Scope 声明指出:

顶层 class 或接口(§7.6)的 scope 是所有 class 和 package 中的接口声明,其中声明了顶层 class 或接口。

那么为什么org.something.a.String不与java.lang.String冲突,因为java.lang.*被导入每个编译单元?

7.3 编译单元声明:

每个编译单元隐式导入每个公共 class 或在预定义 package java.lang 中声明的接口,就好像声明import java.lang.*; 在任何 package 声明之后立即出现在每个编译单元的开头。 因此,所有这些类和接口的名称在每个编译单元中都可以作为简单名称使用。

请注意,它明确地将导入语句声明为import java.lang.*; ,这被称为“按需导入”:

7.5.2 Type-Import-on-Demand 声明

按需类型导入声明允许根据需要导入命名为 package、class 或接口的所有可访问类和接口。

 TypeImportOnDemandDeclaration: import PackageOrTypeName. *;

6.4.1 Shadowing谈到按需导入:

按需类型导入声明永远不会导致任何其他声明被隐藏。

因此java.lang.String永远不能隐藏一个名为 class 的String (对于java.lang中的其他类也类似)


有了这两个班级

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());
    }
}

你不能运行org.something.a.Main因为那个main方法的签名是public static void main(org.something.a.String[] args) ,但是为了能够运行那个 class 签名必须是public static void main(java.lang.String[] args) 请注意,大多数时候您可以将方法编写为public static void main(String[] args)因为当前 package 中没有 class String隐藏java.lang.String

如果您将代码更改为

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());
    }
}

那么您将能够运行 class 因为现在 main 方法的签名是正确的。

运行此代码将 output

 org.something.a.String

要查看java.lang.Stringorg.something.a.String之间的区别,您应该将main方法扩展为

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());
    }
}

这将 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

Stringjava.lang.String的映射是怎么消失的?

它不会消失,它不会首先显现出来。

重要的是要理解所有这些 import 语句对于我们懒惰的程序员来说只是一个方便的功能。

形式为import java.util.List; 让我们告诉编译器:

你看,只要我将List写成一个简单的、不合格的 class 名称,我希望你使用java.util.List作为合格的 class 名称。

此导入语句不会阻止您使用 class java.awt.List - 它只是意味着您始终必须使用完全限定的 class 名称java.awt.List如果您想使用特定的 class。它不会编译过程没有一点改变——内部编译过程总是只使用完全限定的 class 名称。

import java.lang.*; 这隐含地是每个编译单元的第一个 import 语句更加懒惰。 它告诉编译器:

看,任何时候我将String写成一个简单的、不合格的 class 名称,而您在当前 package 或单一类型导入语句中都找不到String class 的定义,请查看所有按需导入的包,无论您是否可以在任何这些包中找到这样的 class。 如果您能准确找到一个这样的 class,请使用 class 完全限定的 class 名称。

为了完整起见:如果你能找到一个这样的 class ,我写信的原因是:没有人阻止你写

import java.awt.*;
import java.util.*;

并且编译器仍然会愉快地编译您的代码 - 直到您尝试使用简单的 class 名称List的那一刻。 一旦您尝试同时使用 import 语句和简单的 class name List ,编译器将因错误而中止,因为对于这两个 import 语句,它无法知道您是否希望 class name List引用java.awt.Listjava.util.List .列表。

在您的情况下(其中 class String与 class Main位于同一 package 中)此原因不适用。 没有隐式import org.something.a.*


** 如果java.lang.String从不隐藏,那么它在哪里 go..**

java.lang.String始终存在。 只是简单的名字String不再表示java.lang.String ,而是从当前的package指代class String

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM