接口只是“语法糖”吗?
我最近主要在玩PHP和Python。
我在学习面向对象编程中的接口,但我看不出使用它有什么好处。
多个对象可以实现同一个接口,但多重继承不也能做到这一点吗?
如果我可以在Python中检查一个对象是否有某个方法,而这个对象又继承了多个类,那我为什么还要创建一个“没有实现”的接口呢?这个接口主要是个“合同”。
接口是在其他语言中创建的,是因为那些语言不支持多重继承吗?还是说我漏掉了什么更重要的东西?
9 个回答
即使在像Python这样的鸭子类型语言中,接口也能更清晰地表达你的意图。如果你有多个实现,并且它们共享一组方法,接口可以很好地记录这些方法的外部行为,给这个概念起个名字,让这个概念变得具体。
如果没有明确的接口,你系统中一个重要的概念就没有实际的表现形式。这并不是说你必须使用接口,但接口确实提供了这种具体性。
首先,最重要的是,不要把Python和Java进行比较。它们是不同的编程语言,语法和用法也不同。进行比较只会导致一些混淆的问题,比如你试图把Python中没有的东西和Java中必须有的东西进行对比。
这就像比较数字7和颜色绿色一样。它们都是名词,但除此之外,你会发现很难把它们放在一起比较。
简单来说就是:
Python不需要接口。
而Java是需要接口的。
多个对象可以实现同一个接口,但多重继承就不能这样做吗?
这两个概念几乎没有关系。
我可以定义很多类,它们共享一个公共接口。在Python中,由于“鸭子类型”,我不需要确保它们都有一个共同的父类。
接口是对不同类层次结构的“意图”的声明。它提供了一个公共的规范(可以由编译器检查),但这并不属于简单的类层次结构。它允许多个类层次实现一些共同的特性,并在这些特性上具有多态性。
在Python中,你可以选择使用或不使用接口进行多重继承。多重继承可以包含接口类,也可以不包含接口类。
而Java根本没有多重继承。它使用一种完全不同的技术,叫做“混入”。
如果我可以在Python中检查一个对象是否有某个方法,而这个对象又继承了多个类,那我为什么还要创建一个“没有实现”的接口呢?这主要是一个“合同”。
如果你在Python中创建一个接口,它可以算是一种正式的合同。也就是说,所有子类都必须按照接口的要求去实现。
当然,傻瓜是完全可以撒谎的。他们可以继承接口,但却错误地实现所有内容。没有什么能阻止那些不负责任的行为。
在Java中,你创建接口是为了让多个对象类有共同的行为。因为在Python中你不需要告诉编译器太多,所以这个概念在Python中并不适用。
接口是在其他语言中创建的,因为它们不提供多重继承吗?
因为这两个概念没有关系,所以很难回答这个问题。
在Java中,他们使用“混入”来代替多重继承。接口允许一些额外功能的混入。这是接口的一种用途。
接口的另一个用途是将“是什么”和“做什么”分开。类层次结构定义了一个对象“是什么”,而接口层次结构定义了一个类“做什么”。
在大多数情况下,“是什么”和“做什么”是相同的,所以没有区别。
但在某些情况下,一个对象“是什么”和“做什么”是不同的。
接口的有用性和静态类型的有用性是直接相关的。如果你在使用像PHP或Python这样的动态类型语言,接口其实并没有显著提升语言的表达能力。也就是说,任何可以用接口描述的程序,都可以在不使用接口的情况下以几乎相同的方式表达出来。
因此,Python有一个比较模糊的“协议”概念(就是遵循某种模式的实现,比如迭代协议),本质上和接口差不多,但由于缺乏编译时检查的其他好处,它的价值是有限的。
而在静态类型语言中,接口是必不可少的,它可以让实现和具体的实现细节分开。在静态语言中,所有表达式的类型必须在编译时确定,因此通常在那个时候就要绑定到具体的实现,这样就限制了运行时的灵活性。接口定义了如何访问功能,而不定义具体的实现,这样静态语言就可以在没有访问实现的情况下证明表达式是正确的。
如果没有接口(或者像C++的纯虚函数这样的等效形式),静态类型语言的表达能力会受到严重限制。实际上,许多实现(比如Win32和COM)基本上是通过在结构中存储函数指针来重现接口和虚拟调度的功能(从而手动实现C++的虚函数和虚表调用)。在这种情况下,表达能力有很大差别,因为需要在程序中做很多更改才能表达相同的概念。
接口只是类型多态性的一种例子,而且是相对有限的一种。在支持参数多态性(也叫泛型)的语言中,你可以实现更多功能。(例如,C#的LINQ如果没有泛型接口是无法实现的。)想了解更强大的同类功能,可以看看Haskell的类型类。