Java基础之多态和泛型浅析

时间:2022-06-29 22:09:41

Java基础之多态和泛型浅析

一、前言:

  楼主看了许多资料后,算是对多态和泛型有了一些浅显的理解,这里做一简单总结

二、什么是多态?

  多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口(这里所谓的接口是楼主自己发明的,这里不是局限于Java的interface,可以把它看作为广义定义的对象源)的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数实现的。但是在Java中楼主认为基于类之间的继承关系
  多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。
  简单的来说多态就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
  把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
  赋值之后,父类型的引用就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。
  举个例子:从一个基类中派生,响应一个虚命令,产生不同的结果。
  比如从某个基类派生出多个子类,其基类有一个虚方法Tdoit,然后其子类也有这个方法,但行为不同,然后这些子类对象中的任何一个可以赋给其基类对象的引用,或者说将子对象地址赋给基类指针,这样其基类的对象就可以执行不同的操作了。实际上你是在通过其基类的引用来访问其子类对象的,你要做的就是一个赋值操作。
使用继承性的结果就是当创建了一个类的家族,在认识这个类的家族时,就是把子类的对象当作基类的对象,这种认识又叫作upcasting(向上转型)。这样认识的重要性在于:我们可以只针对基类写出一段程序,但它可以适应于这个类的家族,因为编译器会自动找出合适的对象来执行操作。这种现象又称为多态性。而实现多态性的手段又叫称动态绑定(dynamic binding)。
  简单的说,建立一个父类对象的引用,它所指对象可以是这个父类的对象,也可以是它的子类的对象。java中当子类拥有和父类同样的函数,当通过这个父类对象的引用调用这个函数的时候,调用到的是子类中的函数。
  多态,意味着一个对象有着多重特征,可以在特定的情况下,表现不同的状态,从而对应着不同的属性和方法。
 
 
  多态所展示的特性示例:
  Parent pa = new Child_A();
  pa.simpleCall()则显然是调用Child_A的方法;
  Parent pa = new Child_B();
  pa.simpleCall()则是在调用Child_B的方法。所以,我们对于抽象的父类或者接口给出了我们的具体实现后,pa 可以完全不用管实现的细节,只访问我们定义的方法,就可以了。事实上,这就是多态所起的作用,可以实现控制反转。这在大量的J2EE轻量级框架中被用到,比如Spring的依赖注入机制。

三、什么是泛型?

  一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用多种类型的代码,这种刻板的限制对代码的约束就会很大。
  在面向对象编程语言中,上面介绍的多态是一种泛化机制。例如,你可以将方法的参数类型设为基类,那么,该方法就可以接受从这个基类中导出的任何类作为参数。这样的方法更加通用一些。可应用的地方也多一些。在类的内部也是如此,凡是需要说明类型的地方,如果都使用基类,确实能够具备很好的灵活性。但是如果考虑除了final类(不能扩展),其他任何类都可以被扩展,所以这种灵活性大多数也会有一些性能损耗。
  有时候,拘泥于单继承体系,也会使程序受限太多。如果方法的参数是一个接口,而不是一个类,这种限制就放松了许多。因为任何实现了该接口的都能够满足该方法,这也包括暂时还不存在的类
  在Java5之后,引入了泛型这个概念:泛型实现了参数化类型的概念,使代码可以应用于多种类型,泛型这个概念的术语是:“适用于许多许多的类型”。泛型通过解耦类或方法所使用的类型之间的约束。使类或方法具备最广泛的表达能力

   为什么要使用泛型?

  在Java5之前,泛型程序设计是用继承实现的。例如ArrayList类只维护一个Object引用的数组:

  //before generic classes

  public class ArrayList {

    private Object[] elementData;

    ...............

    public Object get(int i) {.......}

    public void add(Object object) {.........}

  }  

  这种方法有两个问题,当获取一个值时必须进行强制类型转换

  ArrayList files = new ArrayList();

  .......................

  String fileName = (String)files.get(0);

  此外,这里没有错误检查。可以向数组列表中添加任何类的对象。

  files.add(new File("......"));

  对于这个调用,编译和运行都不会出错。然而在其他地方,如果将get的结果强制类型转换为String类型,就会产生一个错误。泛型提供了一个更好的解决方案:

  Java5以后,ArrayList有一个类型参数来指定元素的类型:

  ArrayList<String> files = new ArrayList<String>();(在Java7及以后的版本中,构造函数中可以省略泛型类型:ArrayList<String> files = new ArrayList<>();)

  这显然使得代码具有更好的可读性。人们一看这个数组列表中包含的是String对象。

三、多态与泛型的区别:

  其实多态和泛型也没有什么根本的区别,如果非要说说区别,那就说说吧:

  泛型和多态比较容易混淆, 简单来说:泛型就是类型参数化, 好处是编译器会检查参数类型.多态就是多个类由继承(实现接口)得到的一致外观, 好处是简化代码, 隔绝修改实际上泛型和多态没有直接关系, 使用泛型在代码中就能按照指定类型的外观操作对象.

  比较常用的泛型类型就是List<T>和Map<T, T>, 说个简单的例子吧:

  List<String> list = ....; //具体类型, 就跟多态没什么关系了

  for (String str : list) {

    System.out.println(str.substring(1));

  }

  List<Animal> list = ...; //地球上但凡讲OO的都拿这个说事儿... 成员就是小猫小狗狮子老虎什么的

  for (Animal animal : list) {

     animal.say(); //喵, 汪....., 多态

  }

  多态可以分为编译时多态和运行时多态,泛型是对类型的抽象,属于编译时多态。

  比如说下面的私有字段x, 它的数据类型在编译的时候是动态决定的,具有多态性。

  class A<T>{ private T x;} A<string> a = new A<string>();

  多态还可以分为:行为多态和属性多态。

  下面的p在给它赋值前是不知道它的性别的,在给它赋不同的值的时候就表现出属性多态了。

  与此同时,也具有了行为的多态, Walk().男人和女人走路的方式是不一样的,所以是多态了。

  基类: Class Person, public virtual method Walk() {}

  子类: Man, Woman

     Person p = new Man();

     Person p = new Woman();

  转载请注明出处,谢谢!