深入java核心 java中多态的实现机制

上传人:wt****50 文档编号:33542208 上传时间:2018-02-15 格式:DOCX 页数:11 大小:96.61KB
返回 下载 相关 举报
深入java核心 java中多态的实现机制_第1页
第1页 / 共11页
深入java核心 java中多态的实现机制_第2页
第2页 / 共11页
深入java核心 java中多态的实现机制_第3页
第3页 / 共11页
深入java核心 java中多态的实现机制_第4页
第4页 / 共11页
深入java核心 java中多态的实现机制_第5页
第5页 / 共11页
点击查看更多>>
资源描述

《深入java核心 java中多态的实现机制》由会员分享,可在线阅读,更多相关《深入java核心 java中多态的实现机制(11页珍藏版)》请在金锄头文库上搜索。

1、1.派生类对象的方法调用必须通过一个基类类型的变量进行。2.调用的方法必须在派生类中被定义。3.调用的方法也必须被声明为基类的一个成员。4.基类和派生类中对应的方法的签名必须相同。5.基类和派生类的方法的返回对象类型必须相同或者返回对象类型必须是协变的。6.派生类的方法的访问说明符不能比基类有更多的限制。深入 Java 核心 Java 中多态的实现机制多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到 Java 多态性。在 Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了 Java 实现运行时多态性的动态方法调度;今天我们再次深入 Java 核心,一起学习 Ja

2、va 中多态性的实现。“polymorphism(多态)”一词来自希腊语,意为“多种形式”。多数 Java 程序员把多态看作对象的一种能力,使其能调用正确的方法版本。尽管如此,这种面向实现的观点导致了多态的神奇功能,胜于仅仅把多态看成纯粹的概念。Java 中的多态总是子类型的多态。几乎是机械式产生了一些多态的行为,使我们不去考虑其中涉及的类型问题。本文研究了一种面向类型的对象观点,分析了如何将对象能够表现的行为和对象即将表现的行为分离开来。抛开 Java 中的多态都是来自继承的概念,我们仍然可以感到,Java 中的接口是一组没有公共代码的对象共享实现。多态的分类 多态在面向对象语言中是个很普遍

3、的概念.虽然我们经常把多态混为一谈,但实际上 有四种不同类型的多态。在开始正式的子类型多态的细节讨论前,然我们先来看看普通面向对象中的多态。Luca Cardelli 和 Peter Wegner(On Understanding Types, Data Abstraction, and Polymorphism一文的作者, 文章参考资源链接)把多态分为两大类-特定的和通用的-四小类:强制的,重载的,参数的和包含的。他们的结构如下:在这样一个体系中,多态表现出多种形式的能力。通用多态引用有 相同结构类型的大量对象,他们有着共同的特征。特定的多态涉及的是小部分没有相同特征的对象。四种多态可做以下

4、描述:强制的:一种隐式做类型转换的方法。重载的:将一个标志符用作多个意义。参数的:为不同类型的参数提供相同的操作。包含的:类包含关系的抽象操作。我将在讲述子类型多态前简单介绍一下这几种多态。强制的多态强制多态隐式的将参数按某种方法,转换成编译器认为正确的类型以避免错误。在以下的表达式中,编译器必须决定二元运算符+所应做的工作:2.0 + 2.02.0 + 22.0 + 2第一个表达式将两个 double 的 操作数相加;Java 中特别声明了这种用法。第二个表达式将 double 型和 int 相加。Java 中没有明确定义这种运算。不过,编 译器隐式的将第二个操作数转换为 double 型,

5、并作 double 型的加法。做对程序员来说十分方便,否则将会抛出一个编译错误,或者强制程序员显式的将 int 转换为 double。第三个表达式将 double 与一个 String 相加。Java 中同样没有定义这样的操作。所以,编译器将 double 转换成 String 类型,并将他们做串联。强制多态也会发生在方法调用中。假设类 Derived 继承了类 Base,类 C 有一个方法,原型为 m(Base),在下面的代码中,编译器隐式的将 Derived 类的对象 derived 转化为 Base 类的对象。这种隐式的转换使 m(Base)方法使用所有能转换成 Base 类的所有参数。

6、1. C c = new C(); 2. 3. Derived derived = new Derived(); 4. 5. c.m( derived ); 并且,隐式的强制转换,可以避免 类型转换的麻烦,减少编译错误。当然,编译器仍然会优先验证符合定义的对象类型。 重载的多态 重载 允许用相同的运算符或方法,去表示截然不同的意义。+在上面的程序中有两个意思:两个double 型的数相加;两个串相连。另外还有整型相加,长整型,等等。这些运算符的重载,依赖于编译器根据上下文做出的选择。以往的编译器会把操作数隐式转换为完全符合操作符的类型。虽然 Java 明确支持重载,但 不支持用户定义的操作符重

7、载。Java 支持用户定义的函数重载。一个类中可以有相同名字的方法,这些方法可以有不同的意义。这些重载 的方法中,必须满足参数数目不同,相同位置上的参数类型不同。这些不同可以帮助编译器区分不同版本的方法。编译器以这种唯一表示的特征来表示不同的方法,比用名字表示更为有效。据此,所有的多态行为都能编译通过。强制和重载的多态都被分类为特定的多态,因为这些多态都是在特定的意义上的。这些被划入多态的特性给程序员带来了很大的方便。强制多态排除了麻烦的类型和编译错误。重载多态像一块糖,允许程序员用相同的名字表示不同的方法,很方便。参数的多态 参数多态允许把许多类型抽象成单一的表示。例如,List 抽象类中,

8、描述了一组具有同样特征的对象,提供了一个通用的模板。你可以通过指定一种类型以重用这个抽象类。这些参数可以是任何用户定义的类型,大量的用户可以使用这个抽象类,因此参数多态毫无疑问的成为最强大的多态。乍一看,上面抽象类好像是 java.util.List 的功能。然而,Java 实际上并不支持真正的安全类型风格的参数多态,这也是 java.util.List 和 java.util 的其他集合类是用原始的 java.lang.Object 写的原因(参考我的文章A Primordial Interface? 以获得更多细节)。Java 的单根继承方式解决了部分问题,但没有发挥出参数多态的全部功能。

9、Eric Allen 有一篇精彩的文章“Behold the Power of Parametric Polymorphism”,描述了 Java 通用类型的需求,并建议给 Sun 的 Java 规格需求#000014 号文档Add Generic Types to the Java Programming Language.(参考资源链接)包含的多态 包含多态通过值的类型和集合的包含关系实现了多态的行为.在包括 Java 在内的众多面向对象语言中,包含关系是子类型的。所以,Java 的包含多态是子类型的多态。在早期,Java 开发者们所提及的多态就特指子类型的多态。通过一种面向类型的观点,我

10、们可以看到子类型多态的强大功 能。以下的文章中我们将仔细探讨这个问题。为简明起见,下文中的多态均指包含多态。面向类型观点图 1 的 UML 类图给出了类和类型的简单继承关系,以便于解释多态机制。模型中包含 5 种类型,4 个类和一个接口。虽然 UML 中称为类图,我把它看成类型图。如Thanks Type and Gentle Class, 一文中所述,每个类和接口都是一种用户定义的类型。按独立实现的观点(如面向类型的观点),下图中的每个矩形代表一种类型。从实现方法看,四种类型运用了 类的结构,一种运用了接口的结构。 图 1:示范代码的 UML 类图以下的代码实现了每个用户定义的数据类型,我把

11、实现写得很简单。用这样的类型声明和类的定义,图 2 从概念的观点描述了 Java 指令。Derived2 derived2 = new Derived2();图 2 :Derived2 对象上的引用上文中声明了 derived2 这个对象,它是 Derived2 类的。图 2 种的最顶层把 Derived2 引用描述成一个集合的窗口,虽然其下的 Derived2 对象是可 见的。这里为每个 Derived2 类型的操作留了一个孔。Derived2 对象的每个操作都去映射适当的代码,按照上面的代码所描述的那样。例 如,Derived2 对象映射了在 Derived 中定义的 m1()方法。而且还

12、重载了 Base 类的 m1()方法。一个 Derived2 的引用变量无权访问 Base 类中被重载的 m1()方法。但这并不意味着不可以用 super.m1()的方法调用去使用这个方法。关系到 derived2 这个引用的变量,这个 代码是不合适的。Derived2 的其他的操作映射同样表明了每种类型操作的代码执行。既然你有一个 Derived2 对象,可以用任 何一个 Derived2 类型的变量去引用它。如图 1 所示,Derived, Base 和 IType 都是 Derived2 的基类。所以,Base 类的引用是很有用的。图 3 描述了以下语句的概念观点。Base base =

13、 derived2;图 3:Base 类引用附于 Derived2 对象之上虽然 Base 类的引用不用再访问 m3()和 m4(),但是却不会改变它 Derived2 对象的任何特征及操作映射。无论是变量 derived2 还是 base,其调用 m1()或 m2(String)所执行的代码都是一样的。两个引用之所以调用同一个行为,是因为 Derived2 对象并不知道去调用哪个方法。对 象只知道什么时候调用,它随着继承实现的顺序去执行。这样的顺序决定了 Derived2 对象调用 Derived 里的 m1()方法,并调用 Derived2 里的 m2(String)方法。这种结果取决于对

14、象本身的类型,而不是引用的类型。尽管如此,但不意味着你用 derived2 和 base 引用的效果是完全一样的。如图 3 所示,Base 的引用只能看到 Base 类型拥有的操作。所以,虽然 Derived2 有对方法 m3()和 m4()的 映射,但是变量 base 不能访问这些方法。运行期的 Derived2 对象保持了接受 m3()和 m4()方法的能力。类型的限制使 Base 的引用不能在编译期调用这些方法。编译期的类型检查像一套铠甲,保证了运行期对象只能和正确的操作进行相互作用。换句话说,类型定义了对象间相互 作用的边界。多态的依附性类型的一致性是多态的核心。对象上的每一个引用,静

15、态的类型检查器都要确认这样的依附和其对象的层次是一致的。当一个引用成功的依附于另一个不同的对象 时,有趣的多态现象就产生了。(严格的说,对象类型是指类的定义。)你也可以把几个不同的引用依附于同一个对象。在开始更有趣的场景前,我们先来看一下下 面的情况为什么不会产生多态。多个引用依附于一个对象图 2 和图 3 描述的例子是把两个及两个以上的 引用依附于一个对象。虽然 Derived2 对象在被依附之后仍保持了变量的类型,但是,图 3 中的 Base 类型的引用依附之后,其功能减少了。结论很明显: 把一个基类的引用依附于派生类的对象之上会减少其能力。一个开发这怎么会选择减少对象能力的方案呢?这种选

16、择是间接的。假设有一个名 为 ref 的引用依附于一个包含如下方法的类的对象:用一个 Derived2 的参数调用 poly(Base)是符合参数类型检查的:方法调用把一个本地 Base 类型的变量依附在一个引入的对象上。所以,虽然这个方法只接 受 Base类型的参数,但 Derived2 对象仍是允许的。开发这就不必选择丢失功能的方案。从人眼在通过 Derived2对象时所看到的情况,Base 类型引用的依附导致了功能的丧失。但从执行的观点看,每一个传入poly1(Base)的参数都认为是 Base 的对象。执行机并不在乎有多个引用指向同一 个对象,它只注重把指向另一个对象的引用传给方法。这些对象的类型不一致并不是主要问题。执行器只关心给运行时的对象找到适当的实现。面向类型的观点展示 了多态的巨大能力。附于多个对象的引用让我们来看一下发生在 poly1(Base)中的多态行 为。下面的代码创建了三个对象,并通过引用传给poly1(Base):poly1(Ba

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 生活休闲 > 科普知识

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号