TIJ——Chapter Eight:Polymorphism
The twist
|_Method-call binding
Connecting a method call to a method body is called binding. When binding is performed before the program is run(by the compiler and linker, if there is one), it's called early binding. You might not hava heard the term before because it has never been an option with procedural languages. C compilers hava only one kind of method call, and that's early binding.
When a language implements late binding, there must be some mechanism to determine the type of the object at run time and to call the appropriate method. That is, the compiler still doesn't know the object type, but the method-call mechanism finds out and calls the correct method body. The late-binding mechanism varies from language to language, but you can imagine the some sort of type information must be installed in the objects.
All method binding in Java uses late binding unless the method is static or final (private method are implicitly final). This means that ordinarily you don't need to make any decision about whether late binding will occur-it happens automatically.
|_Pitfall:"overriding" private methods
Here's something you might innocently try to do:
public class PrivateOverride
{
private void f() {System.out.println("private f()");} public static void main(String[] args)
{
PrivateOverride po = new Derived();
po.f();
}
} class Derived extends PrivateOverride
{
public void f() {System.out.println("public f()");}
} /*Output:
private f() */
You might reasonably expect the output to be "public f()", but a private method is automatically final, and is also hidden from the derived class. So Derived's f() in the case is a brand new method; it's not even overloaded, since the base-class version of f() isn't visible in Derived. If you want to get the "public f()", you can modify that code po.f() to ((Derived)po).f().
The result of this is that only non-private methods may be overridden, but you should watch out for the appearance of overriding private methods, which generates no compiler warnings, but doesn't do what you might expect. To be clear, you should use a different name from a private base-class method in your derived class.
|_Pitfall: fields and static methods
Once you learn about polymorphism, you can begin to think that everything happens polymorphically. However, only ordinary method calls can polymorphic. If you access a field directly, that access will be resolved at compile time.
Although this seems like it could be a confusing issue, in practice it virtually never comes up. For one thing, you'll generally make all fields private and so you won't access them directly, but only as side effects of calling methods, In addition, you probably won't give the same name to base-class field and a derived-class field, because its confusing.
If a method is static, it doesn't behave polymorphically, static methods are associated with the class, and not the individual objects.
Constructors and polymorphism
|_Order of constructor calls
Even though constructors are not polymorphic(they're actually static methods, but the static declaration is implicit), it's important to understand the way constructors work in complex hierarchies and with polymorphism.
A constructor for the base class is always called during the construction process for a derived class, chaining up the inheritance hierarchy so that a constructor for every base class is called.
The order of constructor calls for a complex object is as follows:
1. The base-class constructor is called. This step is repeated recursively such that the root of the hierarchy is constructed first, followed by the next-derived class, etc., until the most-derived class is reached.
2. Member initializers are called in the order of declaration.
3. The body of the derived-class constructor is called.
The order of the constructor calls is important. When you inherit, you know all about the base class and can access any public and protected members of the base class.This means that you must be able to assume that all the members of the base class are valid when you're members of all parts of the object have been built. Inside the constructor, however, you must be able to assume that all members that you use hava been built. The only way to guarantee this is for the base-class constructor to be called first. Then when you're in the derived-class constructor, all the members you can access in the base class have been initialized. Knowing that all members are valid inside the constructor is also the reason that, whenever possible, you should initialize all member object(that is, objects placed in the class using composition) at their point of definition in the class. If you follow this practice, you will heap ensure that all base class members and members objects of the current object have been initialized.
|_Inheritance and cleanup
When you override dispose()(the name I have chosen to use here; you may come up with something better) in an inherited class, it's important to remember to call the base-class version of dispose(), since otherwise the base-class cleanup will not happen.
|_Behavior of polymorphic methods inside constructors
Conceptually, the constructor's job is to bring the object into existence(which is hardly an ordinary feat). Inside any constructor, the entire object might be only partially formed-you can only know that the base-class objects have been initialized. If the constructor is only one step in building an object of a class that's been derived from that constructor's class, the derived parts have not yet been initialized at the time that the current constructor is being called. A dynamic bound method call, however, reaches "outward" into the inheritance hierarchy. It calls a method in a derived class. If you do this inside a constructor, you call a method that might manipulate members that haven't been initialized yet-a sure recipe for disaster.
You can see the problem in the following example:
class Glyph{
void draw() {System.out.println("Glyph.draw()");}
Glyph(){
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw(){
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors{
public static void main(String[] args){
new RoundGlyph(5);
}
}
/* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/
The order of initialization described in the earlier section isn't quite complete, and that's the key to solving the mystery. The actual process of initialization is:
1. The storage allocated for the object is initialized to binary zero before anything else happens.
2. The base-class constructors are called as described previously. At this point, the overridden draw() method is called(yes, before the RoundGlyph constructor is called), which discovers a radius value of zero, due to Step 1.
3. Member initializes are called in the order of declaration.
4. The body of the derived-class constructor is called.
For avoid this problem, a good guideline for constructors is, "Do as little as possible to set the object into a good state, and if you can possibly avoid it, don't call any other methods in this class." The only safe methods to call inside a constructor are those that are final in the base class.(This also applies to private methods, which are automatically final.) These cannot be overridden and thus cannot produce this kind of surprise. You may not always be able to follow this guideline, but it's something to strive towards.
Covariant return types
Java SE5 adds covariant return types, which means that an overridden method in a derived class can return a type derived from the type returned by the base-class method.
Designing with inheritance
A general guideline is "Use inheritance to express differences in behavior, and fields to express variations in state".
(END_XPJIANG)
TIJ——Chapter Eight:Polymorphism的更多相关文章
- TIJ——Chapter One:Introduction to Objects
///:~容我对这个系列美其名曰"读书笔记",其实shi在练习英文哈:-) Introduction to Objects Object-oriented programming( ...
- TIJ——Chapter Seven:Reusing Classes
Reusing Classes 有两种常用方式实现类的重用,组件(在新类中创建存在类的对象)和继承. Composition syntax Every non-primitive object has ...
- TIJ——Chapter Two:Everything Is an Object
If we spoke a different language, we would perceive a somewhat different world. Ludwig Wittgenstein( ...
- TIJ——Chapter Twelve:Error Handling with Exception
Exception guidelines Use exceptions to: Handle problems at the appropriate level.(Avoid catching exc ...
- TIJ——Chapter Eleven:Holding Your Objects
Java Provides a number of ways to hold objects: An array associates numerical indexes to objects. It ...
- TIJ——Chapter Five:Initialization & Cleanup
Method overloading |_Distinguishing overloaded methods If the methods hava the same name, how can Ja ...
- TIJ——Chapter Fourteen:Type Information
Runtime type information(RTTI) allows you to discover and use type information while a program is ru ...
- TIJ——Chapter Thirteen:Strings
String 对象是不可修改的,对于被String 重载的'+' 和'+=' 运算符来说,当你用它们来连接两个String 对象的时候,它在底层并不会对于每一次连接均生成一个String 对象,取而代 ...
- TIJ——Chapter Ten:Inner Classes
先提纲挈领地来个总结: 内部类(Inner Class)有四种: member inner class,即成员内部类.可以访问外部类所有方法与成员变量.生成成员内部类对象的方法:OuterClass. ...
随机推荐
- JAVA Day8
1. 引用数据类型需要new 2. 字符串使用的3种方式 String s = "hello world"; String s = new String(); String s = ...
- Hello cnblog!
Test Markdown #!/usr/env/python # coding: utf-8 # 这是一个测试文件 print "hahah" def t(): print &q ...
- Mac下各种网络命令的使用
Mac下各种网络命令的使用(http://blog.51yip.com/linux/745.html) pingwww.baidu.com 会一直ping下去,和windows不一样, windows ...
- hibernate 的SessionFactory的getCurrentSession 与 openSession() 的区别
1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会. 2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而ope ...
- windows下调试virtualbox的虚拟机串口
1.我不知道其他人是怎么实现的,我是这么实现的. 2.下载一个叫做VSPD的软件,其作用是在windosw上虚拟几个串口出来. 下载完了安装,安装完了注册,如果不是花钱买来的,那就自己想办法注册吧.我 ...
- 水坑式攻击-APT攻击常见手段
所谓“水坑攻击”,是指黑客通过分析被攻击者的网络活动规律,寻找被攻击者经常访问的网站的弱点,先攻下该网站并植入攻击代码,等待被攻击者来访时实施攻击. 水坑攻击属于APT攻击的一种,与钓鱼攻击相比,黑客 ...
- mysql锁 实战测试代码
存储引擎 支持的锁定 MyISAM 表级锁 MEMORY 表级锁 InnoDB 行级锁 BDB 页面锁 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高,并发度最低.行级锁:开销 ...
- Windows2003远程桌面单会话登录
在使用远程桌面连接到Windows2003的时候默认设置是同一用户可以进行多会话登录. (在winxp.win7及以后版本的windows中已经变成单会话登录.) 同用户多会话登录在管理上带来诸多麻烦 ...
- IOS网络第五天 AFN-02-文件上传,底部弹出窗体,拍照和相册获取图片上传
************** #import "HMViewController.h" #import "AFNetworking.h" @interface ...
- TotalCommander 之 配置
一.设置配置界面: 1.进入设置界面 点击菜单栏的配置,然后再点击配置里面的选项,便会出现Total Commander设置的界面. 2.设置字体 刚开始,大家会发现这不是我们熟悉的字体啊 ...