Thinking in Java(8)——Polymorphism

Upcasting 向上转型

Upcasting may “narrow” that interface, but it cannot make it anything less than the full interface to the base class.

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.
  • Late binding means that the binding occurs at run time, based on the type of object, which is also called dynamic binding or runtime binding.
  • All method binding in Java uses late binding unless the method is static or final (private methods are implicitly final).
Producing the right behavior
Extensibility
Pitfall: “overriding” private methods
public class PrivateOverride {
	private void f() { print("private f()"); }
	public static void main(String[] args) {
		PrivateOverride po = new Derived();
		po.f();
	}
}
class Derived extends PrivateOverride {
	public void f() { print("public f()"); }
} /* Output:
private f()
  • Only non-private methods may be overridden.
  • Overriding private methods, which generates no compiler warnings, but doesn’t do what you might expect.
  • You should use a different name from a private base-class method in your derived class.
Pitfall: fields and static methods
class Super {
	public int field = 0;
	public int getField() { return field; }
}
class Sub extends Super {
	public int field = 1;
	public int getField() { return field; }
	public int getSuperField() { return super.field; }
}
public class FieldAccess {
	public static void main(String[] args) {
		Super sup = new Sub(); // Upcast
		System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField());
		Sub sub = new Sub();
		System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getField() 
		+ ", sub.getSuperField() = " + sub.getSuperField());
	}
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~
  • When upcasting, any field accesses are resolved by the compiler, and are thus not polymorphic.
  • If a method is static, it doesn’t behave polymorphically.

Constructors and polymorphism

Constructors are actually static methods.

Order of constructor calls
  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.
Inheritance and cleanup

The order of disposal should be the reverse of the order of initialization, in case one subobject is dependent on another.

Behavior of polymorphic methods inside constructors

If you call a dynamically-bound method inside a constructor, the overridden definition for that method is used. However, the effect of this call can be rather unexpected because the overridden method will be called before the object is fully constructed. This can conceal some difficult-to-find bugs.

class Glyph {
	void draw() { print("Glyph.draw()"); }
	Glyph() {
		print("Glyph() before draw()");
		draw();
		print("Glyph() after draw()");
	}
}
class RoundGlyph extends Glyph {
	private int radius = 1;
	RoundGlyph(int r) {
		radius = r;
		print("RoundGlyph.RoundGlyph(), radius = " + radius);
	}
	void draw() {
		print("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 actual process of initialization:

  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 initializers are called in the order of declaration.
  4. The body of the derived-class constructor is called.

As a result, 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.

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.

class Grain {
	public String toString() { return "Grain"; }
}
class Wheat extends Grain {
	public String toString() { return "Wheat"; }
}
class Mill {
	Grain process() { return new Grain(); }
}
class WheatMill extends Mill {
	Wheat process() { return new Wheat(); }
}
public class CovariantReturn {
	public static void main(String[] args) {
		Mill m = new Mill();
		Grain g = m.process();
		System.out.println(g);
		m = new WheatMill();
		g = m.process();
		System.out.println(g);
	}
} /* Output:
Grain
Wheat
*///:~

Designing with inheritance

Substitution vs. extension

The extended part of the interface in the derived class is not available from the
base class.

Downcasting and runtime type information

Downcast is used to retrieve the type information— that is, to move back down
the inheritance hierarchy.
In some languages (like C++) you must perform a special operation in order to get a type-safe downcast, but in Java, every cast is checked!
At run time downcasting is checked, and if it cannot be cast you will receive ClassCastException.
This act of checking Polymorphism types at run time is called runtime type identification (RTTI).

class Useful {
	public void f() {}
	public void g() {}
}
class MoreUseful extends Useful {
	public void f() {}
	public void g() {}
	public void u() {}
	public void v() {}
	public void w() {}
}
public class RTTI {
	public static void main(String[] args) {
		Useful[] x = {
			new Useful(),
			new MoreUseful()
		};
		x[0].f();
		x[1].g();
		// Compile time: method not found in Useful:
		//! x[1].u();
		((MoreUseful)x[1]).u(); // Downcast/RTTI
		((MoreUseful)x[0]).u(); // Exception thrown
	}
} ///:~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值