假设我们有一个称为interfaceAnimal,它有两个名为move()makeSound()的方法。
这意味着我们可以在move()类型的变量上发送makeSound()Animal消息,并且我们只能将实现Animal的类的对象分配给Animal类型的变量。
现在我的问题是,Java是否可以不强制要使用多态性的类来实现interface
例如,为什么Java不能像下面这样实现多态:
我们只需要创建一个Animal interface,然后我们就可以将想要的任何对象分配给Animal类型的变量,只要该对象具有方法move()makeSound()即可,例如:

Animal animal1;

/* The Java compiler will check if Dog have the methods move() and makeSound(), if yes then
   compile, if no then show a compilation error */
animal1 = new Dog();

animal1.move();
animal1.makeSound();
注意:我以Java为例,但我通常谈论的是所有OOP语言。另外,我知道我们可以使用从父类(super class)继承的子类实现多态(但这与使用interface基本上是相同的想法)。

最佳答案

有多种获取多态性的方法。您最熟悉的一个是包含多态性(也称为子类型多态性),程序员通过某种扩展子句明确地说“X is-a Y”。您可以在Java和C#中看到这一点;两者都让您选择对表示形式和API(extends)还是仅对API(implements)使用这样的is-a。
还有参数多态性,您可能已将其视为泛型:使用单个声明定义Foo<T>类型的族。您可以在Java/C#/Scala(泛型),C++(模板),Haskell(类型类)等中看到这一点。
某些语言具有“鸭子类型”,在这种情况下,他们更愿意从结构上确定类型,而不是查看声明(“X is-a Y”)。如果契约(Contract)说“要成为Iterator,则必须具有hasNext()next()方法”,则在这种解释下,提供这两种方法的任何类都是Iterator,无论是否声明。这与您描述的情况相符。这是Java设计人员可以选择的选择。
具有模式匹配或运行时反射的语言可以表现出一种特殊的多态性(也称为数据驱动的多态性),您可以在其中定义不相关类型的多态行为,例如:

int length(Object o) {
    return switch (o) {
        case String s -> s.length();
        case Object[] os -> os.length;
        case Collection c -> c.size();
        ...
    };
}
在这里,length在一组特定类型上是多态的。
也可以有一个明确的声明“X is-a Y”,而不必将其放在X的声明中。Haskell的类型类是这样做的,在这里,不是X声明“I is a Y”,而是一个单独的声明的instance明确表示“X是Y(如果对编译器不明显,这里是将X功能映射到Y功能的方法。”)这种实例通常称为见证人;通常是见证人。它是X的Y罩的见证者。Clojure的协议(protocol)是相似的,并且Scala的隐式参数起着相似的作用(“为我提供CanCopyFrom[A,B]的见证者,否则在编译时失败”)。
所有这些的意义在于,有多种方法可以实现多态,有些语言会选择自己喜欢的语言,而另一些语言则支持不止一种,等等。
如果您的问题是为什么Java为什么选择显式子类型而不是鸭子类型,那么答案就很明确:Java是一种旨在用组件构建大型系统(如C++)的语言,而组件则需要对其边界进行严格检查。松散匹配的情况是,因为双方碰巧具有相同名称的方法,所以与显式声明相比,建立程序员意图的方法不那么可靠。另外,Java语言的核心设计原则之一是“阅读代码比编写代码更重要”。声明“implementation Iterator”可能会做更多的工作(但要多做很多),但是它使读者更清楚地知道您的设计意图是什么。
因此,这是我们现在称为“仪式”的一种折衷,以提高可靠性并更清晰地捕获设计意图。

10-07 12:47
查看更多