行为子类型:
子类型多态:客户端可用统一的方式处理不同类型的对象。
栗子!
在java中编译器关于这部分有以下规则(静态检查实现):
另外LSP也适用于指定的方法:
荔枝1:
荔枝2:
LSP是一种对子类型关系的特殊限制,称为强行为子类型化:
其中包含以下几种限制:
协变:父类型到子类型:越来越具体的spec,返回值类型:不变或者变的更加具体,异常类型也是如此。
这么说太晦涩了。简而言之就是上面讲的内容。对于子类重写方法返回值要么相同,要么返回他的子类型。抛出的异常同样肯定是父类中抛出异常的子类,就这么点干货==
嘛,还是来看两个荔枝吧:
子类型重写方法,返回值变成了父类型方法返回值的子类型,任何对象都是Object子类型呦……
第七章我们会讲到,所有抛出的异常都会是Throwable这个超类的子类。怎么样很简单吧。
逆变(也叫反协变):父类型到子类型:越来越具体的spec(这点与协变相同,重点在后面),对于参数类型,要采取相反的变化,要么不变,要么越来越抽象。
这点也解释一下吧,其实干货也少的可怜,对于参数类型,如果满足逆变,那么子类型方法中的参数类型要么与父类型相等,要么是父类型参数的抽象类。
形象点说吧,现在有个Man类,有一个findGirlFriend(CodeGirl Girl)方法,因为大多数人都喜欢可爱活泼的程序员妹子呀。现在一个CodeDog对象继承Man类,他可以码代码,可是他觉得他应该找个女朋友了。所以他可以找一个CodeGirl然后在一起。不过他既然是一个继承Man的新类,那么他一定要有自己的个性啊!凭什么一定要和大众一样喜欢CodeGirl呢?我要求低,我也可以找个Female呀,实在找不到我找个Animal总可以吧。再找不到Object纸片人总有吧!
上面的栗子中就是反协变,怎么样也很简单吧。
看个例子吧(哪有我的变态形象)
怎么样是不是感觉自己理解本质了?java太简单了吧!然而却想多了……
这种反协变看起来很符合逻辑,但是!!!!!!!!!!!!!!!!!!!
这种反协变java中不允许!!!!!!!!!!!因为他会让重载规则复杂化。(其实也可以理解,就拿上面的栗子,找个Object类型真的辣眼睛……更重要的是不符合道德伦理,三纲五常blablabla)
另外再次提醒一下,如果你在编译器中打出上面栗子的那个代码发现没有发生静态检查报错,看看是否在重写方法前加上了@Override,没加的话编译器会按重载规则来判断……
总结:
另外说一下数组是协变的,也就是说:
最后一步不会发生静态检查报错,其会在动态检查发生。
然后说一下泛型中的LSP:
注意,泛型是类型不变的,什么意思?举个例子:
ArrayList
这是为什么?在此需要提的是,编译完成后,编译器会丢弃类型参数的类型信息,因此这种类型的信息在运行中是不可用的,这个过程称为类型擦除(type erasure),所以泛型不是协变的。
类型擦除的一个例子:
其实根本就没有什么泛型这种类型,所谓泛型也就是存在java内部的一种机制,在将其在编译过程中会将泛型转化为用户需要的类型,如果您足够屌可以去看一下编译后的汇编指令,就会发现在定义的时候泛型就已经被转化为了用户所需的类型了。
关于这部分记住这张图就好啦:
然后说一下泛型的通配符:
很简单的小东西,我是这么理解的,通配符顾名思义,就是什么都可以替代,你可以在用法上将?理解为Object类型,但是和它有本质不同,object类型是一切对象的父类,然而?可以有 super XXX>或者 extends XXX>用法来代表其取代类在继承等方面的性质。
首先我们先来了解一下java中的比较器(Comparator):
栗子:
如果ADT需要比较大小,或者要放到Arrays或Collections里进行排序,可以实现Comparator接口,并且重写里面的compare方法即可。
当然还有一种方法,就是让你的ADT去实现Comparable接口,然后重写其中的compareTo方法。其与上者的区别在于他不需要构建新的Comparator类,比较代码放在ADT内部。
好,现在来谈一下委派(Delegation):
其定义是一个对象请求另一个对象的功能。
委派是复用的一种常见的形式,其主要分为两种:
概念和了解起来都不难,码点代码的人都知道这些事情,先给出一个流程图来说明:
下面来说一下委派与继承的区别:
继承可以理解为通过新操作来扩展基类或者覆盖父类操作,而委派则是调用一个对象的操作发送给另一个对象。
都是作为复用的手段,没有决定的优劣,根据具体情况决定使用哪个,很多设计模式都是使用委派和继承的组合。
注意:如果只需要复用父类的一小部分方法,可以通过委派机制实现。一个类不需要继承另一个的全部方法,可以通过委派机制调用部分方法。
复合继承原则:又称复合复用原则(Composite Reuse Principle)CRP原则。类应该通过其组合(通过包含实现所需功能的其他类的实例)实现多态行为和代码重用,而不是从基类或父类继承。
委托可以理解为发生在object层面上,而继承则发生在class层面上。
关于委派和继承的各种组合关系这里涉及到很多java的设计模式,由于篇幅等问题,这里不再赘余,如果各位想要了解,之后会有专门介绍java设计模式的章节(下一节)
其中可将委派的用法分为以下几种:
最后讲一下框架:
其分为两种: