没有继承可以实现多态吗?

时间:2012-07-31 02:49:12

标签: java oop inheritance polymorphism

在一次采访中,我被问到是否可以在没有继承的情况下实现多态性。这可能吗?

6 个答案:

答案 0 :(得分:65)

我读过的关于这个主题的最佳解释是Luca Cardelli的一篇文章,这是一位着名的理论家。该文章名为On Understanding Types, Data Abstraction, and Polymorphism

多态性的类型

Cardelli在本文中定义了几种类型的多态:

  • 通用
    • 参数
    • 夹杂物
  • 特设
    • 重载
    • 强制

与继承相关的多态性类别被归类为包含多态性或子类型多态性。

Wikipedia提供了一个很好的定义:

  

在面向对象的编程中,子类型多态或包含   多态性是类型理论中的一个概念,其中一个名称可以表示   许多不同类的实例,只要它们相关联   一些常见的超级课程。包含多态性通常是   通过子类型支持,即不同类型的对象   完全可替代另一种类型的对象(它们的基础   type(s))因此可以通过通用接口处理。   或者,可以通过类型实现包含多态性   强制,也称为铸造。

另一篇名为Polymorphism in object-oriented programming的维基百科文章似乎也回答了你的问题。

在Java中

Java中的这种子类型功能是通过继承类和接口来实现的。尽管Java的子类型特征在继承方面可能并不明显。例如,协方差和与泛型相反的情况。此外,数组是Serializable和Cloneable,尽管在类型层次结构中的任何位置都不明显。还可以说,通过原始扩展转换,Java中的数字运算符是多态的,在某些情况下甚至接受完全不相关的操作数(即字符串和数字或字符串加上一些其他对象的串联)。还要考虑基元的装箱和拆箱的情况。后面这些多态性(强制和重载)的情况与继承完全无关。

实施例

<强>夹杂物

List<Integer> myInts = new ArrayList<Integer>();

这是您的问题似乎引用的情况,即类型之间存在继承或实现关系时,例如ArrayList实现List的情况。

正如我所提到的,当你引入Java泛型时,有时候子类型的规则会变得模糊:

List<? super Number> myObjs = new ArrayList<Object>();
List<? extends Number> myNumbers = new LinkedList<Integer>();

在其他情况下,API中的关系甚至不明显

Cloneable clone = new int[10];
Serializable obj = new Object[10]

即便如此,Cardelli认为,所有这些都是普遍多态的形式。

<强>参数

public <T> List<T> filter(Predicate<T> predicate, List<T> source) {
  List<T> result = new ArrayList<>();
  for(T item : source) {
    if(predicate.evaluate(item)){
         result.add(item);
    }
   return result;
  }
}

可以使用相同的算法来过滤具有各种谓词的所有类型的列表,而无需为每种可能类型的列表重复单行代码。实际列表的类型和谓词的类型是参数化的。使用JDK 8 Preview中提供的lambda表达式查看此示例(为了简化谓词实现)。

filter(x -> x % 2 == 0, asList(1,2,3,4,5,6)); //filters even integers
filter(x -> x % 2 != 0, asList(1L,2L,3L,4L,5L,6L)); //filters odd longs
filter(x -> x >= 0.0, asList(-1.0, 1.0)); //filters positive doubles

根据Cardelli的说法,这是一种普遍的多态性。

<强>胁迫

double sum = 1 + 2.0;

整数和浮点运算完全不同。如果没有某种形式的强制,将plus运算符应用于两个不同类型的操作数是不可能的。

在此示例中,integer和double类型会在没有显式强制转换的情况下自动强制(转换)为double类型。整个表达式被提升为双倍。这是因为在Java中我们有原始的扩展转换。

根据Cardelli的说法,这种形式的自动强制是为plus运算符提供的一种ad-hoc多态。

有些语言甚至无法在没有显式强制转换的情况下对整数和浮点数求和(即AFAIK,SML,顺便说一下,参数多态是解决此类问题的关键)。

<强>重载

double sum = 2.0 + 3.0;
String text = "The sum is" + sum;

这里的加号运算符意味着两种不同的东西,具体取决于所使用的参数。显然,操作员已经超负荷运转。这意味着它具有不同的实现,具体取决于操作数的类型。根据Cardelli的说法,这是为plus运算符提供的一种ad-hoc多态。

当然,这也适用于类中方法重载的形式(即java.lang.Math方法min和max被重载以支持不同的基本类型)。

其他语言

即使继承在某些形式的多态性的实现中起着重要作用,当然它也不是唯一的方法。其他非面向对象的语言提供其他形式的多态性。例如,在动态语言(如Python)中使用duck typing的情况,甚至在诸如Go之类的静态类型语言中使用algebraic data types,或者在诸如SML,Ocaml和Scala或type classes之类的语言中使用multi methods。像Haskell,Clojure中的prototypal inheritance,JavaScript中的{{3}}等语言

答案 1 :(得分:6)

Ad-hoc多态性&gt;运算符重载&gt;没有继承

Ad-hoc多态性&gt;方法重载&gt;没有继承

Ad-hoc多态性&gt;方法覆盖&gt;具有继承性

参数多态性&gt;泛型&gt;没有继承

亚型多态性或包含多态性&gt; <多态分配>具有继承性

亚型多态性或包含多态性&gt; <多晶型返回类型>具有继承性

亚型多态性或包含多态性&gt; <多态参数类型>具有继承性

强制多态性&gt;加宽&gt;有或没有继承

强制多态性&gt;自动装箱和拆箱&gt;没有继承

强制多态性&gt; Var args&gt;没有继承

强制多态性&gt;类型铸造&gt;没有继承

答案 2 :(得分:5)

不确定。在Java中,您可以让两个类实现相同的接口,并且它们的结果是多态的。没有继承任何功能。

public interface Foo {
  public int a();
}

public class A implements Foo {
  public int a() {
    return 5;
  }
}


public class B implements Foo {
  public int a() {
    return 6;
  }
}

然后在其他地方:

Foo x = new A();
System.out.println(x.a())
Foo y = new B();
System.out.println(y.a())

xy都是Foo个,但是当您拨打a()时,它们会有不同的结果。

答案 3 :(得分:0)

是的,我认为他们可能想通过接口了解多态性。因此,如果有2个类从同一个接口实现,那么我们可以在所有我们想到具有这种干涉的对象的地方使用。请参阅维基百科的代码:

// from file Animal.java

public interface Animal {
        public String talk();
}

// from file Cat.java

public class Cat implements Animal {
        @Override
        public String talk() {
                return "Cat says Meow!";
        }
}

// from file Dog.java

public class Dog implements Animal {
        @Override
        public String talk() {
                return "Dog says Woof! Woof!";
        }
}

// from file PolymorphismExample.java

public class PolymorphismExample {

        public static void main(String[] args) {
                Collection<Animal> animals = new ArrayList<Animal>();
                animals.add(new Cat());
                animals.add(new Dog());

                for (Animal a : animals) {
                        System.out.println(a.talk());
                }
        }

}

答案 4 :(得分:0)

静态类型

重载 - 这意味着具有相同名称但签名不同的多个方法可以完成覆盖

class StaticPolyExample
{
void print(int s)
{
    //print s
}

void print(String s)
{
    //print s
}

}

动态类型

重写 - 这意味着超类中的方法将在需要继承的子类中重新定义

class Printer
{
void print(String s)
{
  // prints String
}

}

class diffPrinter extends Printer
{
void print(String s)
{
  // prints String differently
}

}

答案 5 :(得分:0)

函数重载是多态性之一(虽然它不是真正的多态性意味着),它可以在没有继承的情况下实现。

e.g。

class Foo { 
public void Arrest( Animal A){
    /*code...*/
 }  
public void Arrest( Terrorist T ) {
    /*code...*/
 }  

}


from main :

Foo f= new Foo();
f.Arrest( new Lion() );
f.Arrest(new Terrorist());

逮捕方法被调用2次,但代码的执行路径不同。

*同样,这不是真正的多态性形式。 如果没有继承,就无法实现普通的多态性。

相关问题