这几天终于有自己的时间了,赶快更几篇博客,要多多学习新的知识呀,话不多说,进入正题……
协变返回类型
什么是协变返回类型?
子类被覆盖的方法可以返回基类类型的一个导出类型。
乍一看上去整个人都是懵的,之后看了好长时间才把这个意思弄明白,我先写段代码,之后好解释一点儿。
public class Test {
public static void main (String [] args) {
Owner o = new Owner();
Pet p = o.myPet();
System.out.println(p);
o = new ManOwner();
p = o.myPet();
System.out.println(p);
}
}
class Pet {
public String toString() {
return "this is a pet";
}
}
class Cat extends Pet {
public String toString() {
return "this is a cat";
}
}
class Owner {
Pet myPet() {
return new Pet();
}
}
class ManOwner extends Owner {
Cat myPet() {
return new Cat();
}
}
显示一下运行结果:
this is a pet
this is a cat
接下来进入我们刺激的肉眼跑代码流程:
1、Pet是父类,Cat是他的子类,子类重写父类当中的toString方法
2、Owner是父类,ManOwner是子类,子类中和父类有相同的方法myPet,但是返回类型不一样
3、主函数首先创建Owner类型的对象o,之后使用o创建Pet对象,最后调用p的toString方法(直接写p就是调用对象的toString方法),输出的肯定是this is a pet
4、之后使用o创建ManOwner对象,调用方法myPet创建Cat对象,赋值给引用p,那么p对应的toString方法输出的就是this is a cat,但是o的返回类型是Pet类型的,而且父类又使用不了子类的拓展方法,但是输出的结果证明返回的是Cat类型的数据。
这是为什么呢?
原来在jdk5之前,子类覆盖父类的方法签名都是需要完全一样的,但是在jdk5之后就放宽了这一约束,只要方法名相同或者子类方法的返回值是父类方法返回值的子类,那么就可以实现覆盖。
这样,前面的问题就解决了,myPet的方法名是相同的,所以第二次在对象o调用myPet方法的时候,绑定的方法是返回Cat类型的方法,输出结果也就是this is a cat
简单了解状态模式
什么是状态模式?
在程序运行的过程中,一个引用可以和不同的对象绑定起来,其行为也同时发生改变的,以便在运行时期得到动态灵活性,这种设计模式叫做状态模式。
下面还是拿一段代码来解释什么是状态模式。
public class Test {
public static void main (String [] args) {
Stage stage = new Stage();
stage.perform();
stage.change();
stage.perform();
}
}
//状态模式
class Actor {
public void act() { }
}
class HappyActor extends Actor {
public void act() {
System.out.println("happy action");
}
}
class SadActor extends Actor {
public void act() {
System.out.println("sad action");
}
}
class Stage {
private Actor actor = new HappyActor();
public void change() {
actor = new SadActor();
}
public void perform() {
actor.act();
}
}
看看运行结果:
happy action
sad action
现在有一个舞台,上面有戏剧演员和悲剧演员,在舞台(stage)类当中有换场(change)的函数,当换场的时候把actor的绑定对象更改,就实现了运行时成员的状态,这就是状态模式。
向下转型和运行类型识别
我们之前提到过向上转型,那个是把子类当做父类来用,是一种窄化的思想,但是向上转型会丢失信息,子类当中的扩展方法是没办法进行调用的,所以就出现了向下转型
向下转型是一种拓展的思想,通过向下移动(在继承的模式图当中,子类是在下面的)来获取类型的所有信息。
还是老规矩,用代码说话:
public class Test {
public static void main (String [] args) {
perform(new HappyActor());
}
public static void perform(Actor actor) {
HappyActor hactor = (HappyActor) actor;
hactor.laugh();
}
}
//状态模式
class Actor {
public void act() { }
}
class HappyActor extends Actor {
public void act() {
System.out.println("happy action");
}
public void laugh() {
System.out.println("hiahiahia~");
}
}
输出的结果是hiahiahia~
这就是向下转型,在perform方法当中,传进的对象都是Actor类型或者是其子类,在方法当中都转化为Actor类型,之后再**扮演(cast)**成一个HappyActor类型的对象,调用子类对象的扩展方法。
注意:在这里面是扮演,而不是强制转换,二者之间是有很大区别的。
这样虽然实现了访问子类所有信息,但是你必须保证转换的类型和参数传进来的对象是同一类型的,否则可能出现难以预料的结果。
我修改一下perform传进来参数看看。
public static void main (String [] args) {
perform(new SadActor());
}
public static void perform(Actor actor) {
HappyActor hactor = (HappyActor) actor;
hactor.laugh();
}
}
我传进去的是SadActor类型的对象,但是后来扮演成了HappyActor类型的对象,悲剧演员变成喜剧演员了?这肯定不对呀。
所以说向下转型必须要保证转型的正确性。
这篇博客就写到这里了 ,希望对你的学习有帮助,记得点赞哦!!!