抽象类和接口之间的差异

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

很明显,对吧?

不,这不对。我进行了很多采访,我经常问的第一个问题是关于接口和抽象类之间的区别。而且我仍然遇到了很多无法给我正确答案的程序员。

在我看来,即使是初级程序员也应该了解它们,也许不一定了解背后的原因,但仍然 - 特定于特定语言(以及几乎所有 OOP 语言相同)的结构差异应该比已知的更多。

我找到了什么?申请其他职位(有时甚至是高级职位)的候选人不知道差异或只知道几个或一个。

我知道只有我们非常了解的东西才容易,但那些是面向对象的基础知识,我们必须了解它们才能编写设计良好的代码。

那么,让我们谈谈基础知识。

遗产

让我们从接口和抽象类之间最广为人知的区别之一开始。这种区别与继承有关——任何类都可以实现多个接口,但只能扩展一个类并且只能有一个父类。

多类扩展是一种语言特性,存在于一些面向对象的语言中。为什么?因为它带来的问题多于价值。

当一个类有很多父类并且存在我们在多个类中有完全相同的方法声明的情况时,我们将不得不明确地“告诉”哪个是我们感兴趣的。

这样的代码很难维护,因为每当您引入任何更改或重构它时,您都必须仔细检查它。另一方面,如果一个类需要使用相同的方法扩展(至少)两个类,那么我们要么在谈论打破 DRY 规则,我们可以在其他地方提取一些东西,要么我们在搞乱 单一责任原则

好吧,如果需要扩展两个类,我们就是在搞乱 SRP。如果每个人都对一件事负责,那么必须扩展他们两个的东西对……是的,我相信你知道我的意思。

“拜托,既然多类继承如此糟糕,为什么实现多个接口还可以呢?” – 如果你想到这样的问题,我不得不承认这是一个该死的好问题 :)

然而,答案很简单。每个接口都基于功能而不是类——基于实现。因此,即使我们正在实现十个不同的接口并且每个接口都包含相同的方法声明,也不会发生冲突。接口是方法存在的保证,而不是它必须如何实现的方式,这意味着只要你不违反 SRP,你应该可以实现许多接口。

方法的可见性

接口中的所有方法都是公共的,但是对于在抽象类中声明的方法则没有这样的规则。那些可以有任何可见性,除了私人。为什么不私了?因为抽象方法需要在子类中实现,而子类无法访问私有方法。如您所见,这两个想法是相互排斥的。

好的,但让我们回到主题。正如我之前所写,接口是功能的保证,您可以将其视为使用接口的类与实现它的类之间的契约。它保证特定类将实现所有声明的方法。这就是为什么这些方法必须是公开的。任何其他此时都不重要,因为与实现密切相关。

但是,当我们谈论抽象类时,情况并非如此。我们总是可以拥有一组仅在几个方面不同的类,除此之外它们几乎相同并且它们的公共方法的主体也非常相似。当我们发现自己处于这种情况时,我们总是可以创建受保护的方法来保留差异。使用此行为的流行模式之一是 模板方法

声明与定义

接口只能包含方法的声明,抽象类也可以包含方法的定义。

之所以如此,是因为接口专注于提供特定的功能,而抽象类也可以专注于子类实现的相似性,而不仅仅是它们的功能。

常量

在接口和抽象类中,定义常量值都没有问题。这是因为这些值不依赖于特定对象,而是对所有对象都是相同的。

属性

抽象类可以包含属性,而接口不能。原因与声明和定义相同。

概括

除了显示差异之外,我还试图解释它的来源。这不仅仅是因为发明特定语言的人的心血来潮。它直接来自于这些结构背后的想法和概念。

我希望我没有忘记任何事情。但如果是,那么请在您的评论中提请我注意,我会尽快消除错误。

而且,好吧,祝你面试顺利 :)