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 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)

时间: 2025-01-09 21:27:34

TIJ英文原版书籍阅读之旅——Chapter Eight:Polymorphism的相关文章

TIJ英文原版书籍阅读之旅——Chapter One:Introduction to Objects

///:~容我对这个系列美其名曰“读书笔记”,其实shi在练习英文哈:-) Introduction to Objects Object-oriented programming(OOP) is part of this movement toward using the computer as an expressive medium. This chapter will introduce you to the basic concepts of OOP, including an over

TIJ英文原版书籍阅读之旅——Chapter Two:Everything Is an Object

If we spoke a different language, we would perceive a somewhat different world. Ludwig Wittgenstein(1889-1951) You manipulate objects with references Although you treat everything as an object, the identifier you manipulate is actually "reference&quo

TIJ英文原版书籍阅读之旅——Chapter Five:Initialization & Cleanup

Method overloading |_Distinguishing overloaded methods If the methods hava the same name, how can Java know which method you mean? There's a simple rule : Each overloaded method must take a unique list of argument types. |_Overloading with primitives

TIJ英文原版书籍阅读之旅——Chapter Three:Operators

Operators 本章节比较简单,所以简单的做一些笔记: 几个要点: 1.When the compiler sees a String followed by a "+" followed by a  non-String, it attempts to convert the non-String into a String. 2.比较对象的内容使用equals()方法,前提是该对象所对应的类重写了 Object类中的equals()方法,并且实现了对对象内容进行比较.绝大多数

TIJ英文原版书籍阅读之旅——Chapter Seven:Reusing Classes

Reusing Classes 有两种常用方式实现类的重用,组件(在新类中创建存在类的对象)和继承. Composition syntax Every non-primitive object has a toString() method, and it’s called in special situations when the compiler wants a String but it has an object. Inheritance syntax You’re always do

TIJ英文原版书籍阅读之旅——Chapter Eleven:Holding Your Objects

Java Provides a number of ways to hold objects: An array associates numerical indexes to objects. It holds objects of a known type so that you don't have to cast the result when you're looking up an object. It can be multidimensional, and it can hold

【资源分享】CLR.via.C#(第3版)英文原版+中文译本+随书代码

看完了<叩响C#之门>,并利用学到了知识解决了工作中的一个麻烦. 觉得C#没有想象中那么难,于是打算进一步学好它. 这几天在四处请教好书推荐. 毕竟对于我来说,没有那么多时间用来自己寻找教材. 有不少热心的朋友向我推荐了许多书籍,我也都一一找来看了看. 最终,我决定看<CLR.via.C#(第3版)> 它或许更适合我目前的阶段,而设计模式之类的对我来说还有点早. 中午在亚马逊买了中文版,但始终不太放心,觉得还是能有英文原版对照会更容易理解. 根据我的经验,有时候英文会比中文更好理解

《教父》中的经典台词以及英文原版

<教父>是我最喜欢的小说已经电影之一,在网上搜集了其中的经典台词和英文原版: Don't hate your enemy, or you will make wrong judgment.不要憎恨你的敌人,否则你将做出错误的判断. Don't let anybody know what you are thinking. 不要让任何人知道你在想什么. You make the choice, and this is your price. 你做出了这个决定,这是你的代价. Everything

暑假多看看英文原版电影

对于大多数的学生,暑假的到来,意味着可以好好的进行休息了,而在暑假中可以做的事情是很多的,放松或许是主要目的,但是也不要放弃学习的机会,暑假看英文原版电影则是这样的一种选择. 英文原版电影比中文配音的更具有观赏性,而且其纯英文的语境则是有利于学生从中学到一些英语知识的,也许只是只言片语,但也同样是收获的.在暑假这样的环境中,放松的去看几部经典的英文原版电影,既可以享受生活,有可以有新知识可以学习. 当然,仅仅看一遍这样的带字幕的原版电影,其对英语的熟悉还是不够的,最好可以同一部电影看上两遍,并对