我对Java和C#处理命名空间的概念感到困惑。
首先,某些编程语言中命名空间污染的例子:
using namespace std
用于C ++。
业内人士对此表示不满,但在开始编程时,初学者仍然被教导要这样做。 Here's a SO question about the advice for handling global namespaces
import math.*
当我参加Python课程时,我被告知不建议这样做,因为它会污染命名空间,并且首先允许访问数学库中没有Math.functionname
的所有方法,但在编写带有方法的方法时可能会导致冲突重复的名字。它显然导致了解释器的更多工作,因为它导入了所有函数,甚至那些未使用的函数。
在Ocaml中打开模块 在命名中也可以在顶层或ML文件Might cause conflicts中执行此操作。特别是如果要写一个图书馆。
问题:
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
一样
或在Java中我经常看到:import java.util.linkedlist
,甚至是import java.util.*
如果我的课程在其他地方使用,这些会不会导致命名空间污染?或者是因为它们只会“污染”特定的类范围而不是其他可能导入或继承我的类的类?
我试着寻找答案,但找不到一个答案,我可能会错误地搜索搜索。
编辑4/20/2015 :
正如@RealSkeptic所提到的,事实证明也不鼓励使用Java通配符。 source
除了下面接受的答案和答案之外,Java和C#中的导入本身也包含在内,因此即使有人使用通配符类型的导入将未使用的方法名称添加到命名空间,它也不会影响其他类。在类级别,如果名称中发生冲突,Java和C#编译器将引用错误引用模糊导入,并且在问题解决之前无法进一步编译(例如通过重命名函数)。
答案 0 :(得分:4)
我认为Java / C#与C ++有不同的哲学。让我解释一下。
当您使用Java导入内容时,只会在代码的这一部分中导入它。假设您在包A
中有一个课程packageA
。此外,A
导入包B
和packageB
导入B
中的类java.util.LinkedList
。在C ++中,A
会包含要使用B
的标头文件,在此标头文件中,某些#include
会有LinkedList
。因此,在LinkedList
中可以看到A
。在Java中,情况并非如此:类A
必须自己包含java.util.LinkedList
才能使用它。也就是说,您仍然可以获得命名空间污染,但它们更本地化(通常是每个文件)。这就是为什么在使用import
时应该避免使用星号的原因。
我无法为C#提供合格的答案,因为我不使用它,但我认为他们的理念是相似的。
答案 1 :(得分:4)
正如其他人已经指出的那样,命名空间污染问题在Java中并不像在C ++中那么突出。命名空间污染是C ++中的一个问题(因此首先称为“污染”)的主要原因是它可能导致其他模块中的错误。这在Why is “using namespace std;” considered bad practice?中有更详细的解释。
(这里有关的事情是,这可能不仅仅是指编译错误:对于编译错误,你强制做某事并解决歧义。真的有关事情是它可能导致代码仍然正确编译,但之后只是调用错误的函数!)
在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
的引用是不明确的。
顺便说一句,同样的问题也会出现在static imports中。它更精致,更微妙,碰撞更有可能。这就是为什么他们说你应该非常谨慎地使用静态导入 。
旁注:当然,这些碰撞和含糊不清可以很容易地解决。您始终可以使用完全限定名称。大多数现代Java IDE都提供组织/优化导入的功能。例如,在Eclipse中,您始终可以按 CTRL + Shift + O ,这将取决于偏好设置中的设置 - > Java->代码样式 - >组织导入)将所有通配符导入替换为各个
。答案 2 :(得分:1)
我认为在Java和C#中,导入是简单的,但编译器会使用完全限定名称识别类中的每个引用。所以你写new LinkedList<>()
,但编译器理解这个类中你的意思是new java.util.LinkedList.LinkedList<>()
。
这解决了你提到的问题,我相信,因为包的类的全名绝对必须是唯一的。
答案 3 :(得分:1)
正在运行的 Java 程序中的每个类加载器都会维护自己的名称空间,其中填充了
支持的所有类的名称。 name-space
是由loaded classes
维护的Java Virtual Machine
的一组唯一名称。例如,一旦Java Virtual Machine
将名为Volcano
的类加载到特定name-space
中,就无法将名为Volcano
的其他类加载到同一name-space
中}。您可以将多个Volcano
类加载到Java Virtual Machine
中,因为您可以通过创建多个multiple name-spaces
在Java application
内创建class loaders
。如果在正在运行的Java application
中创建三个单独的名称空间(三个类加载器各一个),则通过将一个Volcano
类加载到每个名称空间中,您的程序可以加载三个不同的{ {1}}您的应用程序中的类。
如果您对应用中的所有类使用相同的类加载器,则所有Volcano
将仅加载到classes
一旦。 same name space
不会在后续访问中加载所有这些Class Loader
。一个classes
中的classes
也无法与其他name space
中的类进行互动。