为什么Java不允许像C ++中那样使用标头


18

我有一个问题,除了以下不符合我要求的答案外,我没有找到答案:

“因为詹姆斯·高斯林不想”

我知道Java可以有接口(只有纯虚函数,没有属性),但这与类定义不完全一样。


14
您想要他们的原因是什么?

19
为了有更多的时间进行剑术;)
dan04

6
我想大多数C ++开发人员都希望摆脱已有40年历史的文本替换引擎,因为每个cpp文件都会复制数百个kLoC,从而导致C ++的编译时间很长。实际上,C ++ 11已考虑使用合适的模块系统,但由于时间不足而放弃了该模块系统。我想它会再次出现。
2011年

我想它会再次出现。实际上,WG21(ISO C ++工作组)有一个研究组,专门用于进一步评估/开发“模块”概念:SG2“模块”。太糟糕了,它的当前状态是休眠
Max Truxa 2015年

Answers:


46

以下答案不符合我的要求:“因为James Gosling不想这样做。”

不过,这是正确的答案。语言设计团队(Gosling,Sheridan,Naughton,后来的Bill Joy,Ken Arnold等)认为标头引起的问题比他们解决的更多。因此,他们设计了它们,并证明了他们可以创建一种不需要它们的完全有用的语言。

Java语言环境白皮书的2.2.1节中

用Java编写的源代码很简单。没有预处理器,没有#define和相关功能,没有typedef,并且没有这些功能,不再需要头文件。Java语言源文件提供了其他类及其方法的定义,而不是头文件。

冗余定义,保持文件同步,定义冲突,隐藏定义-在Java中这些都不会发生,因为您没有标题。如果要查看裸类定义,则可以直接从.java文件生成一个类定义-例如,大多数IDE会在边栏中显示类的结构,这等同于同一件事。


6
然后,感谢您的回答,标题引起了更多的问题,因为:冗余定义,保持文件同步,定义冲突,隐藏定义。这就是为什么不允许这样做?
EtienneNoël

2
请注意,关于下一次C ++委员会会议的讨论很多,因为他们将考虑使用一个新的“模块”系统,该系统比包含(与Java,C#等程序包有一些相似之处)的系统更简单,更高效,但仍然会很复古。与include兼容。也就是说,至少在理论上,可以使用更好的编译系统来使C ++编译更好/更高效。我想Gosling是对的,而且C ++必须找到一种方法来修复include系统。
克拉姆

5
它不是完全可用。Java构建系统无法确定更改代码后必须重新编译哪些文件。IDE将确定需要更改代码的文件,而不是需要重新编译的文件。如果方法签名发生更改,但更改与旧签名在代码上兼容(例如,将参数类型从float更改为double),则需要进行全新构建以防止MethodNotFoundException。
凯文·克莱恩

1
@kevin:但是通常可以无需花费太多就可以重建所有内容。与C ++(但与世界上几乎所有其他编译语言一样)不同,Java编译不需要花费很长时间,因为部分编译是对开发工作流程非常有价值的优化。
Donal Fellows 2012年

1
@Donal:确实Java编译非常快,但是我讨厌猜测我是否应该进行完全重新编译。构建应该每次都能正常工作。
凯文·克莱恩

16

在C ++中,真正不需要在单独的文件中包含类定义和声明。这仅意味着,至少在C天之内,您可以对代码进行一次上下扫描来进行解析。在没有随机访问存储的机器上,这很重要!

具有标头还允许您通过提供标头来将接口发布到代码库,而无需透露源代码。不幸的是,在C ++中,您还必须揭示导致出现诸如pimpl恐怖之类的解决方案的私有数据成员。

已经尝试建立一个C ++环境,其中所有内容都存储在数据库类型结构中,没有文件,但没有流行。


我知道,但是至少您可以用C ++而不是Java来做到;那是我的主要询问。感谢您的回答。
EtienneNoël,

14

由于DRY原理。在Java中,使用包(或类)中的类所需的信息包含在.class文件中。创建包含相同信息的单独的头文件将涉及在两个地方重复。


不幸的是,您经常想重复它-想像一下wsdl文件,idl文件等。一个描述了您可以使用的接口,另一个描述了实现。C ++标头是(较差的)接口定义。
gbjbaanb 2012年

6

在每种语言中,创建最终的二进制代码都有两个阶段-编译和链接(当然,有加载,但是在这里影响不大)。在编译时,只需要在适当的位置放置钩子(将要调用的函数的规范)即可。当两个真实代码都可用时,链接器实际上它们加入。到目前为止,C ++和Java之间没有区别。

还有就是然而,需要C ++有声明和定义分开。如果将实现保留在头文件中,并且头文件发生更改,则需要重新编译与其链接的代码。好像定义在单独的文件中一样,只需重新链接代码即可。

了解C ++确实具有静态链接的选项,这意味着目标代码与调用应用程序一起固定。请注意,在C和C ++中,在头文件中进行编程甚至执行#include都是无效的。这仅意味着您需要考虑如何与这些目标文件进行链接。

Java中的情况非常不同。每个类文件都使用.class文件进行编译。确实,需要在.class文件中用作标头部分的调用方类函数编译。但是,在Java中,仅在给定类文件字节码指定的情况下,仅在运行时(虚拟机)内部完成最终链接。

看到这个这个


4

有效的接口包括头文件;因此,定义与二进制文件相同,并且不能不同步。这是Java最好的设计决策之一,但是令人讨厌的是,没有办法将这些声明捆绑在一起以实现紧凑性和一致性。


1

包含一个很好的理由是将您可能要重用的代码(例如通用定义)与特定于给定项目的代码分开。Java希望您只为每个文件指定一个类或接口,并且这主要减少了对包含标头的需要-因为您将已经拥有共享文件中自己的共享部分。

同样,编译器和构建系统可能希望缓存预编译的标头,以避免多次分析它们。


1
我想你可以在接口存储在共享的项目,然后在独立项目实现它们
亚历山大·米尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.