Java 或 C# 中是否存在命名空间污染(如 C++ 中)

Does namespace pollution in Java or C# exist (like in C++)?

本文关键字:C++ 污染 存在 是否 Java 命名空间      更新时间:2023-10-16

我一直对Java和C#如何处理命名空间的概念感到困惑。

首先,一些编程语言中命名空间污染的例子:

  1. using namespace stdC++。

    业内的每个人都对此不屑一顾,但初学者在开始编程时仍然被教导这样做。这是一个关于处理全局命名空间的建议的 SO 问题

  2. import math.*在 Python 中

在学习 Python 类时,我被告知不建议这样做,因为它会污染命名空间,并且首先允许访问数学库中的所有方法而不Math.functionname但在编写具有重复名称的方法时可能会导致冲突。它显然也给解释器带来了更多的工作,因为它导入了所有功能,甚至是那些未使用的功能。

  1. 在Ocaml中打开模块 在顶层或 ML 文件中执行此操作也可能导致命名冲突。特别是如果编写库。

  2. JavaScript 命名空间污染

问题:

C#和Java中是否存在"命名空间污染"(即在编写方法时导入大量可能导致冲突的方法)(它们在许多方面相似)?为什么不呢?

当然,相关的问题太相似了,不能问另一个问题:

-是因为也许我们必须明确@Override事情还是有某种预防措施?

-或者它存在,但它不是一回事,因为它不会像"使用命名空间 std"那样造成那么大的破坏,而且我不知道它在学术课程之外的软件开发中相对较新?

我发现自己在 C# 中using了很多库,以避免必须重新键入变量的命名空间,例如XElement

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//the ones above are auto-generated by Visual Studio, too
using System.Xml.Linq;

将避免我每次创建 XElement 时都不得不做System.Xml.Linq.XElement。就像我们总是在C++中做std::cout一样

或者在我经常看到的爪哇中:import java.util.linkedlist,甚至import java.util.*

如果我的类在其他地方使用,这些不会导致命名空间污染吗?还是因为它们只会"污染"该特定类范围,而不是可能导入或继承我的类的其他类?

我尝试搜索答案,但找不到答案,但我可能措辞错误。

编辑 4/20/2015

正如@RealSkeptic所提到的,事实证明Java通配符导入也是不鼓励的。

除了下面接受的答案和答案之外,Java 和 C# 中的导入包含在它们内部,因此即使有人使用通配符类型的导入将未使用的方法名称添加到命名空间,也不会影响其他类。在类级别,如果确实发生名称冲突,Java 和 C# 编译器将抛出错误,引用不明确的导入,并且在问题解决(例如,通过重命名函数)之前无法进一步编译。

正如其他人已经指出的那样,命名空间污染的问题在Java中并不像在C++中那样突出。命名空间污染在C++中是一个问题(因此首先称为"污染")的主要原因是它可能会导致其他模块的错误。为什么"使用命名空间 std;"被认为是不好的做法?中对此进行了更详细的解释。

(这里令人担忧的是,这可能不仅涉及编译错误:对于编译错误,您被迫做某事并解决歧义。真正令人担忧的是,它可能会导致代码仍然正确编译,但之后只是调用错误的函数!


在 Java 中,每个import仅影响包含它的文件。这意味着上述污染仍然可以在一个文件中本地发生。(可以说:只是实际造成命名空间污染的作者的问题,这很公平)

类似于上面提到的链接中的情况:假设您正在使用两个库,">foo"和">bar"。出于懒惰(或缺乏最佳实践知识),您正在使用通配符导入:

import foo.*:
import bar.*:
class MyClass {
void someMethod() {
// Assume that this class is from the "foo" librariy, and the
// fully qualified name of this class is "foo.Example"
Example e = new Example();
}
}

现在假设您升级了">bar"库的版本。新版本包含一个名为bar.Example的类。那么上面的代码将无法编译,因为对类Example的引用是不明确的。

顺便说一下,静态导入也会出现同样的问题。它更加微妙和微妙,碰撞的可能性更大。这就是为什么他们说你应该非常谨慎地使用静态导入。


旁注:当然,这些冲突和歧义很容易解决。您始终可以改用完全限定的名称。大多数现代 Java IDE 都提供了组织/优化导入的功能。例如,在 Eclipse 中,您始终可以按CTRL+Shift+O,这将(取决于首选项->Java->Code 样式>组织导入)将所有通配符导入替换为单个通配符导入。

我认为Java/C#与C++有不同的哲学。让我解释一下。

当你在Java中导入某些东西时,你只是在代码的这一部分导入它。假设您在包packageA中有一个类A。此外,A在包packageB中导入类BB导入java.util.LinkedList。在C++中,A将包含一个使用B的头文件,在此头文件中,您将有一个用于某些LinkedList#include。因此,LinkedList将在A中可见.在 Java 中,情况并非如此:类A必须自行包含java.util.LinkedList才能使用它。也就是说,您仍然可以获得命名空间污染,但它们更本地化(通常是每个文件)。这就是为什么在使用时应该避免星号的原因import

我无法给出 C# 的合格答案,因为我不使用它,但我认为它们的哲学是相似的。

我认为在 Java 和 C# 中,导入是一种简写,但编译器使用完全限定名识别类中的每个引用。所以你写new LinkedList<>(),但编译器明白此时在这个类中你的意思是new java.util.LinkedList.LinkedList<>()

我相信,这解决了您提到的问题,因为带有包的类的全名绝对必须是唯一的。

正在运行的Java程序中的每个类装入器都维护自己的命名空间,该命名空间由它已装入的所有的名称填充。

name-space是由Java Virtual Machine维护的一组loaded classes的唯一名称。例如,一旦Java Virtual Machine将一个名为Volcano的类加载到特定name-space中,就不可能将名为Volcano的不同类加载到同一name-space中。但是,您可以将多个Volcano类加载到一个Java Virtual Machine中,因为您可以通过创建多个class loadersJava application中创建multiple name-spaces。如果在正在运行的Java application中创建三个单独的命名空间(三个类加载器中的每一个一个),则通过将一个Volcano类加载到每个命名空间中,程序可以将三个不同的Volcano类加载到应用程序中。

如果对应用程序中的所有类使用相同的类加载器,则所有classes将只装入一次same name spaceClass Loader不会在后续访问中加载所有这些classes。在一个name spaceclasses也无法与其他name space中的类进行交互。