Java 中的包相关标识符路径

Package-relative identifier path in Java

简介

从 C++ 开始,最大的区别之一是作用域的不同:在 C++ 中,每个标识符都是相对于当前作用域的(classnamespace),您可以编写一个绝对路径在开始时使用范围解析运算符 ::

但是,在 Java 中,我无法找到一种方法来访问其他包中的标识符,而无需将其导入当前范围或写入其完整路径。

如果我有几个同名的 class 可能会出现问题(然后,在 C++ 中我会将它们称为 Module1::Foo 和 Module2::Foo,这不是Java 可能,只要我想保持理智,因为 com.company.project.module1.Foo 对我来说太长了)。

代码!

这是从 Java 和 C++(没有 importusing)访问 class Tools.Useless.Foo 的示例

Tools/Useless/Foo.hpp:

namespace Tools {
namespace Useless {
class Foo {         
};
}
}

Tools/Bar.hpp

namespace Tools {
...
// Use Foo with a relative identifier
Useless::Foo foo;
// Use Foo with an absolute identifier.
::Tools::Useless::Foo bar;
...
} 

这就是它在 Java 中的样子:

com/company/project/Tools/Useless/Foo.java:

package com.company.project.Tools.Useless;

public class Foo { }

com/company/project/Tools/Bar.java:

...
// Use Foo with a relative identifier
???
// Use Foo with an absolute identifier.
com.company.project.Tools.Useless.Foo foo;
...

问题

  1. 有没有办法在不指定 Tools.Useless 完整包名的情况下从包 Tools.Useless 访问 Foo(并导入它;因为导入它会将它绑定到当前范围)?
  2. 我做的对吗?同名的几个class怎么用?我应该避免这种情况还是只使用“package.*”导入它们以绕过它?

解决方案

  1. 使用更多包描述性 class 名称(例如 ToolsUselessFoo 而不仅仅是 Foo)。
  2. 使用 import path.to.module.* 而不是 import path.to.module.Foo 导入所有内容,然后访问 Foo 所需的包以解决任何歧义。问题是有时包名称有含义(例如 Tools.Useless.FooTools.Useful.Foo)。

Java 包未嵌套。虽然他们的名字可能有共同的前缀,但这在 Java 编程语言中没有意义。

此外,当存储为文件系统时,它们的 class 文件存储在嵌套目录中这一事实没有其他意义。当 classes 存储在 jar 文件中时,它们存储的条目名称与它们的限定名称相匹配,根本没有真正形成目录(尽管很多工具喜欢将它们呈现为好像在层次结构中以模仿一个文件系统)。

所以包com.company.project.tools.uselesscom.company.project.tools根本没有关系。虽然我们人类倾向于以这种方式组织代码,这里可以假定语义关系(这是一件好事),但在技术层面上存在 none。与任何其他两个包相比,它们之间没有相对寻址,也没有额外的访问权限。事实上,这两个包可能是两个不同模块的一部分(从 Java 9 开始),与名称不太相似的其他两个包相比,访问权限甚至更少。

使用另一个包中的 class 的标准方法是使用 import com.company.project.Tools.Useless.Foo;,然后在 class.

中使用 Foo

目前尚不清楚您看到的是哪个问题,即“自导入后会将其绑定到当前范围”应该是什么意思。 import 语句的存在对代码没有影响。它只告诉编译器,如果范围内没有其他 Foo,如何解析简单名称 Foo 的出现。

换句话说,本地作用域仍然具有优先权,甚至包括继承的成员。此外,在变量和类型可能出现的地方,变量具有优先权。例如。对于 Foo.bar() 的出现,一个名为 Foo 的变量将具有优先权,一个局部变量在同一类型的成员变量之前,一个外部 class 或继承的变量。否则,将使用成员类型、外部类型或继承的成员类型。仅当 none 个存在时,import 语句将用于解析 Foo.

不用说,您应该避免拥有太多具有相同简单名称的项目,以至于您不得不考虑解析过程的细节。这就是为什么命名约定建议变量名以小写字母开头,class 名称以大写字母开头。

是的,避免给 class 起同样的简单名称。一旦您必须在同一个编译单元中处理两个 classes,就无法在整个编译单元中使用其(完全¹)限定名称访问其中一个。 (在 import 中使用 * 对这种情况没有任何帮助)


如前所述,“完全”一词在 Java 中已过时,因为限定名称始终是完整的。