在 Java 中针对接口进行编程与在 C/C++ 中使用头文件的概念相同吗?

Posted

技术标签:

【中文标题】在 Java 中针对接口进行编程与在 C/C++ 中使用头文件的概念相同吗?【英文标题】:Is programming against interfaces in Java the same concept as using header files in C/C++? 【发布时间】:2011-06-29 17:18:33 【问题描述】:

我目前正在处理的 java 代码通常具有类似的结构

文件 Controller.java:

interface Controller ...

文件 ControllerImpl.java:

class ControllerImpl implements Controller ...

但是对于每个接口,只有一个实现。这与在 C/C++ 中使用头文件不同,我将代码拆分为类似

的文件
Controller.hpp
Controller.cpp

据我所知,已经引入了 C/C++ 中的头文件来帮助编译器,而这在 Java 中不再是必需的。头文件也应该有助于代码的可读性,但是拥有一个具有折叠和大纲视图的现代 IDE,这也不再是必需的了。

那么为什么人们又要通过针对接口编程的后门在Java中引入头文件呢?

【问题讨论】:

“每个接口只有一个实现” - 这只是意味着他们还没有编写单元测试;-) @Steve Jessop:但是不使用接口和虚拟实现进行单元测试,他们不能简单地实现一个类并使用模拟框架进行单元测试吗? 是的,因为模拟框架“作弊”,从某种意义上说,它们从一个类中抽象出一个接口而没有正式声明它。为每个具体类定义一个接口,并且从不将接口用于任何其他目的,因此每个接口实际上只有一个类,并且无论将来如何开发代码,都可以被认为是过早的和未使用的灵活性,并且可能被误导。 “过早的柔韧化是一切邪恶的根源!” dev.hubspot.com/bid/7271/… 【参考方案1】:

没有。在 C++ 中,文件(头文件)与类相同。

在 Java 中针对接口进行编程也可以在 C++ 中通过针对abstract base classes 进行编程来完成。

但是,Java 术语“接口”是相当受限制的。基本上,任何函数声明都是一个接口:

void call_me(int times); 

当然,类和其他类型也是如此。

在 C++ 中,您将此类内容分组到标题中,因此接口可以由一个标题组成。但是,它也可能包含多个标题。

【讨论】:

但在 Java 中,我们也可以针对抽象基类进行编程。那么为什么我们需要 Java 中的接口呢? @sbi 这一切都取决于您在头文件中放入的内容。在我看来,尽管 C++ 中的 Java 接口和标头没有任何共同之处,因为 Java 接口可以被同化为 C++ 中的抽象类。关于可以与接口相媲美的标头可以说什么? @asmaier 接口可以通过示例用于多态性。 一个接口可以被许多类实现,而不必从它继承。实现抽象类需要继承,这会使类层次结构非常复杂。 @asmaier:在 C++ 中,一个类可以有多个独立的抽象基类。在Java中它不能,但它可以实现多个独立的接口。因此,您不能像在 C++ 中那样在 Java 中使用抽象基类。正如 David 所说,Java 没有多重继承的原因是它复杂且困难。 Java 中的接口与 C++ 中的编码风格规则基本相同,即“如果最多有一个直接基类具有任何数据成员或非纯虚拟成员函数,则只能使用多重继承”,避免那些困难。【参考方案2】:

接口并非出于保留头文件的愿望。

最接近 Java 的接口支持 http://en.wikipedia.org/wiki/Design_by_contract

【讨论】:

为什么Java不使用抽象类来实现契约式设计? 因为抽象类有实现和封装的值/方法/构造函数,它们不是契约的一部分。 在 Java 中,您只能扩展一个类,但可以扩展任意数量的接口。例如,您不能扩展两个抽象类。 @Peter:“抽象类可以有实现和...” @Peter Lawrey 那么使用接口而不是抽象基类的原因是一个类可以实现多个接口,但不能从多个抽象类继承?【参考方案3】:

接口更像是 C++ 中的抽象基类。

它们在 java 中经常使用的原因是 java 中不存在多重继承(两者都是设计决策)。 c++ 支持多重继承,所以......它可以通过这种方式(以及通过其他一些方式)解决问题。

一旦一个类在java中实现了一个接口,那么它就可以作为类型传递。 c++中的原理相同,虽然写出来的实现略有不同。

因此,接口简化了编程过程,因为可以传递任意对象,只要它们实现了接口(或在 c++ 中派生自特定类)。使用接口,您不需要从公共基类派生——这是一个非常简单且可用的设计。 C++ 中的多重继承对许多开发人员来说是一个陷阱。

【讨论】:

或者换句话说,Java 中的接口(或抽象基类)允许对象拥有多个“类型”,而无需处理多重继承。头文件将某些东西的实际代码与方法/函数签名分开——这样编译器就知道方法是如何被调用的,即使它没有要执行的实际代码。 @averell +1 感谢您的澄清/简化——我有时会写得有点匆忙:“编译器快完成了,得走了!!!” =)【参考方案4】:

在 Java 中,接口定义契约,而类提供契约的实现

大多数合约只有一个有意义或相关的实现;有些甚至假设特定的实现,并且不允许任何其他实现。这些契约及其实现在类中一起定义,没有任何接口。示例:java.lang.String

另一方面,一些合约没有对可能的实现做出任何假设。这些是在接口中定义的。部分实现可以在抽象类中定义,典型的完整实现可以在类中定义,但合约定义在接口中的事实允许您编写自己的合约实现并使用它任何需要合约实例的地方。

在文件级别,接口和类都是编译单元,应该有自己的文件。

正如您现在希望理解的那样,C++ 中头文件和实现文件之间的区别非常不同。

【讨论】:

【参考方案5】:

问题很好,头文件和类/接口/OO 编程之间存在联系,超出了语言的原始语法。

正确的 C++ 程序设计:

放一个类声明,一个 仅在 h 文件中。 为所述 h 文件指定与 类声明。 将类定义放在一个 同名的 cpp 文件 h 文件。

正确的 Java 程序设计:

与 C++ 相同,但也将接口放在自己的文件中。

正确的 C 设计:

在 h 文件中,声明属于特定“代码模块”的函数。 将函数定义放在与 h 文件同名的 c 文件中。 如果编写 C++/Java,您将声明为私有/受保护的所有变量都应该通过“不透明类型/指针”的概念真正私有,或者放置在文件范围内并声明为静态,以便它们可以共享在“代码模块”内的函数之间(尽管这使得代码不可重入)。

如果您不使用上述类和 h 文件之间的密切联系,那么无论您的 OO 设计多么优雅,您的程序都很有可能是一团糟。

【讨论】:

我不确定我是否理解您关于 Java 的观点——“C++ 相同”在我看来,您必须遵循与上述相同的规则。除了,Java 中没有 h 文件。这对我来说毫无意义。【参考方案6】:

C/C++ 中的头文件与类或接口完全无关。

头文件更像是您添加到项目中的引用,或者using 语句。

头文件中存在对象(类)和函数声明是对编译器的一个指令,这样编译器就可以编译该文件而无需查看整个项目。

C++ 中的头文件可以包含类或函数定义、宏、枚举...或许多其他内容,但在概念上与类或接口非常不同。

您可以在 C++ 中使用接口,如下所示:How do you declare an interface in C++?。

...这些定义放在头文件中,但头文件本身是另外一回事。

使用接口而不是继承的原因是许多类可以实现一个接口或许多接口,但您只能从一个类继承。

因此,如果您有许多对象,也就是说,为了可互换,您最终会得到一个非常复杂的类层次结构 - 而有了接口,类可以完全独立地构建,只需要接口链接它们。

【讨论】:

【参考方案7】:

不,针对接口编程与是否使用头文件无关。

明确定义接口将允许双方独立地发展程序。代码体的“接口”表示它遵守的契约。

编程语言从不完全支持接口定义,它们只是有一些语法糖来处理类型正确性方面。一方面,性能方面大部分时间是无法指定的,就像复杂性规则等......(C++ 标准定义了容器和算法的一些复杂性要求)。

这种有限的支持给 Java 留下了关键字interface:因为面向对象是将功能分组到类中,所以将“接口”的概念与对成员函数定义进行分组的东西结合起来是有意义的。多个类可以实现一个给定的接口,一个类也可以实现多个接口。

在 C++ 中,他们甚至都懒得在语言中显式添加“接口”。最接近 Java 的“类接口”的是纯抽象类:一个只有纯虚成员函数的类。由于 C++ 支持多重继承,因此类可以实现多个这些“接口”。

当涉及到将代码分离为头文件和源文件时,这与接口概念完全无关。但实际上:在 C++ 中,“调用合约”主要在头文件中指定。事实上,这可以缩短编译时间(更少的 IO)。但这是编译器技术方面的问题。

【讨论】:

以上是关于在 Java 中针对接口进行编程与在 C/C++ 中使用头文件的概念相同吗?的主要内容,如果未能解决你的问题,请参考以下文章

混合编程JNI之第一篇,Hello world

混合编程JNI之第一篇 Hello world

Java学习之一(引用相关)

JNI 初级接触

JNI 初级接触

“对症下药”的11种新的编程语言