仅仅是因为动态类型我们不需要python中的接口概念(如Java和C#)吗?
9 回答
interface
as 关键字和工件是由 Java 1引入的(C# 从那里得到它)来描述对象必须遵守的契约是什么。
但是,接口一直是面向对象范式的关键部分,它基本上代表了对象必须响应的方法。Java 只是强制执行这种机制来提供静态类型检查。
因此,动态(OO)编程语言确实使用接口,即使它们不会静态检查它们。就像其他数据类型一样,例如在 Ruby 中:
@i = 1;
您不必声明您只需使用它i
的类型。FixNum
接口也是如此,它们只是流动。权衡是,您不能对此进行静态检查,并且故障仅在运行时显示。
另一方面,Go 或 Scala 等语言使用的结构类型(或我称之为静态鸭子类型:P)提供了两全其美的效果。
1. 见 Daniel Earwicker 关于 CORBAinterface
关键字的评论
我们不需要它们,但我们确实支持它们。查看Zope 接口(可以在 Zope 之外使用)。
值得注意的是,与许多人作为第一反应所说的相反,接口可以用来做的不仅仅是记录“类支持哪些方法”。Grzenio 用他关于“实施相同的行为”的措辞触及了这一点。作为这方面的一个具体示例,请查看 Java 接口 Serializable。它没有实现任何方法;相反,它被用作“标记”来指示该类可以安全地序列化。
以这种方式考虑时,拥有使用接口的动态语言可能是合理的。话虽这么说,类似于注释的东西可能是一种更合理的方法。
静态类型语言中使用接口来描述两个原本独立的对象“实现相同的行为”。在动态类型语言中,人们隐含地假设当两个对象有一个具有相同名称/参数的方法时,它会做同样的事情,所以接口是没有用的。
接口构造在静态类型语言中用于教导类型系统在特定的方法调用上下文中哪些对象可以相互替换。如果两个对象实现了相同的方法,但没有通过从公共基类的继承或公共接口的实现相关联,如果您用一个替换另一个,类型系统将在编译时引发错误。
动态语言使用“duck typing”,这意味着在运行时简单地查找该方法,如果它存在且具有正确的签名,则使用它;否则会导致运行时错误。如果两个对象都通过实现相同的方法“像鸭子一样嘎嘎”,那么它们是可替代的。因此,语言没有明确需要通过基类或接口将它们关联起来。
话虽如此,接口作为一个概念在动态世界中仍然非常重要,但它们通常只是在文档中定义,而不是由语言强制执行。偶尔,我看到程序员实际上也创建了一个基类来勾勒出用于此目的的接口;这有助于规范化文档,如果接口的一部分可以根据接口的其余部分来实现,则特别有用。
至少一些动态语言使显式接口有点尴尬的一个关键是动态语言通常可以响应他们事先不知道的消息(错误,“方法调用”),甚至可以做一些事情,比如创建方法在飞行中。了解对象是否会正确响应消息的唯一真正方法是向其发送消息。没关系,因为动态语言认为能够支持这种事情比静态类型检查更好;一个对象被认为可以在特定协议中使用,因为它“已知”能够参与该协议(例如,通过另一个消息给出)。
Perl 具有角色(或特征),它不仅仅是接口,不像 java perl 角色,我们可以有一个实现查看这些链接以获取更多关于 perl 角色的信息
在 C# 和 Java 中,接口只是具有所有抽象方法的抽象类。它们的存在是为了允许伪多重继承而不实际支持成熟的多重继承和多重继承产生的歧义。
Python 支持多重继承,并且有自己的方式来确定当一个方法存在于多个父级中时应该调用哪个父级的方法。
动态语言是鸭子类型的
如果它走路像鸭子,叫起来像鸭子,那它一定是鸭子
http://en.wikipedia.org/wiki/Duck_typing
换句话说,如果您希望一个对象支持 Delete() 方法,那么您可以只使用
obj.Delete()
方法,但如果对象不支持 Delete(),则会出现运行时错误。静态类型语言不允许这样做并引发编译时错误。因此,您基本上可以在类型安全性与更快的开发时间和灵活性之间进行交易。
如果没有接口,您可以在静态语言中执行类似的操作:
void Save(MyBaseClass item)
{
if (item.HasChanges)
item.Save()
}
但这将要求您传递给此方法的每个对象都从 MyBaseClass 继承。由于 Java 或 C# 不支持不太灵活的多重继承,因为如果您的类已经继承了另一个类,它也不能从 MyBaseClass 继承。所以更好的选择是创建一个 ISavable 接口并接受它作为输入参数以确保可以保存该项目。然后你就拥有了最好的:类型安全性和灵活性。
public interface ISavable
{
bool HasChanges {get;set;}
void Save();
}
void Save(ISavable item)
{
if (item.HasChanges)
item.Save()
}
最后一个后门是使用 object 作为参数,如果你不能期望每个项目都会使用你的 save 方法来实现接口。
void Save(object item)
{
if (item.HasChanges)
item.Save()
}
但是再一次,如果有人将您的方法与不兼容的类一起使用,您没有编译时检查,并且可能会出现运行时错误。