Thinking in Java 4th chap8笔记-多态(二)
2.一条通用的准则:用继承表达行为间的差异,用字段表达状态的变化。
3.纯继承与扩展:
1.所谓的纯粹的继承指只有在基类中已经建立的方法才可以才导出类中被覆盖,继承可以确保所有的导出类具有基类的接口,且绝对不会少。->导出类和基类具有一样的接口。也可以认为是一种纯替代,因为导出类完全可以替代基类,而在使用他们时,完全不需要知道关于子类的任何额外信息。->基类可以发送给导出类任何消息,因为二者有着完全相同的接口。我们只需要从导出类向上转型,永远不需要知道正在处理的对象的确切类型。所有这一切,都是通过多态处理的。
2.扩展,可以称为is-like-a关系,因为导出类就像一个是一个基类,它有着相同的基本接口,但是它还具有由额外方法实现的其他特性。虽然这有一种有用且明智的办法(依赖于具体情况),但是它也有缺点。导出类中接口的扩展部分不能被基类访问,因此,一旦向上转型,就不能调用那些新方法。在这种情况下, 如果我们不进行向上转型,这样的问题就不会出现。但是通常情况下,我们需要重新查明对象的确切类型,以便能够访问该类型扩充的方法。
3.向下转型与运行时类型识别:
由于向上转型(在继承层次中向上移动)会丢失具体的类型信息。所以我们就想:通过向下转型,也就是在继承层次中向下移动,应该能够获取类型信息。然而我们知道向上转型是安全的,因为基类不会具有大于导出类的接口。因此我们通过基类接口发送的消息保证都能被接受。但是对于想下转型,如我们无法知道一个几何形状,它确实就是一个圆,它可以是一个三角形,正方形,或者其他一些类型。
要解决这个问题,必须有某种方法来确保向下转型的正确性,使我们不至于贸然转型到一种错误类型,进而发出该对象无法接受的消息。这样做是及其不安全的。
注:C++提供了dynamic_cast函数用于动态转型
但是在Java语言中,所有的转型都会得到检查。所有及时我们只是进行一次普通的加括弧形式的类型转换,在进入运行期时仍然会对其进行检查,以便保证他们的确是我们希望的那种类型。如果不是,就返回一个ClassCastException,类转型异常。这种在运行期间对类型进行检查的行为被称为"运行时类型识别".RTTI.
注:RTTI的内容不仅仅包括转型处理,例如它还提供一种方法,使你可以在试图向下转型之前,查看你所要处理的类型。
10.部分
源码:
package com.book.chap8.multi;
/**
*
*
JavaSE5引入的协变返回类型
*
*返回更具体的谷物类型
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*输出结果:
*Grain
*Wheat
*/
public class CovariantReturn
{
public static void main(Stringargs)
{
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatMill();
g = m.process();
System.out.println(g);
}
}
//谷物
class Grain
{
@Override
public String toString()
{
return "Grain";
}
}
//小麦
class Wheat extends Grain
{
@Override
public String toString()
{
return "Wheat";
}
}
//磨坊
class Mill
{
//返回类型为Grain
Grain process()
{
return new Grain();
}
}
//磨面粉机
class WheatMill extends Mill
{
//注意,这里就用了协变返回类型,覆盖的process方法的返回类型Wheat为Grain的子类
@Override
Wheat process()
{
return new Wheat();
}
}
package com.book.chap8.multi;
/**
*
*讨论多态的缺陷 : 域
*
*这只是讨论的例子,实际的代码中不应该这样写
*1.通常会将域设置为private,而不是此例的public
*2.你可不能不会对基类中的域和导出类中的域赋予相同的名字,因为这会让人感到混淆
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-27
*
*/
public class FieldAccess
{
public static void main(Stringargs)
{
Super sub = new Sub();
//可以看到,这里直接访问sub.field,直接访问域,这是编译解析的;而sub.getField则是方法调用,会动态绑定
System.out.println("sub field:" + sub.field + " sub.getField:" + sub.getField());
Sub sub2 = new Sub();
System.out.println(sub2.field + " " + sub2.getField() + " " + sub2.getSuperField());
}
}
class Super
{
//public 域
public int field = 0;
public int getField()
{
return field;
}
}
class Sub extends Super
{
//1.public 2.与基类取得了相同的名字
public int field = 1;
@Override
public int getField()
{
return field;
}
public int getSuperField()
{
return super.field;
}
}
package com.book.chap8.multi;
import st