要定义多种实现的类型:JAVA有两种机制:接口和抽象类。这两种机制都支持为某些实例方法提供实现,但二者有个重要的区别:要实现由抽象类定义的类型,这个类必须是抽象类的子类。因为Java
只允许单继承,对抽象类的这种限制严重制约了将其用于类型的定义。而接口就宽松很多,只要定义了所有必须的方法,任何类都可以实现。
在正式介绍之前普及两个知识点:缺省方法和抽象方法。
这两个方法是相反的用途:缺省是为了避免子类重写其方法。抽象方法是为了让子类重写方法。
缺省方法(default)案例:
public interface SoyaMilk2 {
default void select() {
System.out.println("选择");
}
}
子类:
public class RedInter implements SoyaMilk2 {
}
抽象方法案例:
public interface SoyaMilk2 {
abstract void addCondiments();
}
子类:
接口优先于抽象类优点:
1.很容易改造现有的类使其实现一个新的接口。
2.接口是定义minxin(混合类型)的理想选择。
3.接口允许构建非层次结构的类型框架。
案例:
public interface Singer {
//唱歌
AudioClip sing(Song s);
}
public interface SongWriter {
//作歌
Song compose(boolean hit);
}
public intreface SingerSongWriter extends Singer, SongWriter {
//弹奏
AudioClip strum();
//激情表演
void actSensitive();
}
抽象类:
Singer.java
public abstract class Singer {
//唱歌
public abstract AudioClip sing(Song s);
}
SongWriter.java
public abstract class SongWriter {
//作歌
public abstract Song compose(boolean hit);
}
SingerSongWriter.java
//在设计的时候感觉很不好设计。因为继承只能继承自一个类,那势必其他的方法需要重新声明
public abstract class SingerSongWriter extends SongWriter {
//唱歌
public abstract AudioClip sing(Song s);
//弹奏
public abstract AudioClip strum();
//激情表演
public abstract void actSensitive();
}
在引入抽象骨架概念之前,先分别看下接口和抽象类的案例实现:
(1)接口案例:
定义接口:
public interface IPeple {
void drink();
void eat();
void ethnicGroup();
}
分别定义子类:
public class AsiaPeple implements IPeple {
@Override
public void drink() {
System.out.println("人都会喝水");
}
@Override
public void eat() {
System.out.println("人都会吃饭");
}
@Override
public void ethnicGroup() {
System.out.println("亚洲人大多数是黄种人");
}
}
public class AmericanPeple implements IPeple {
@Override
public void drink() {
System.out.println("人都会喝水");
}
@Override
public void eat() {
System.out.println("人都会吃饭");
}
@Override
public void ethnicGroup() {
System.out.println("美洲人大多数是白种人");
}
}
问题:
drink和eat方法代码重复性很高,需要改造。
(2)抽象类案例:
定义抽象类:
public abstract class AbsPeple {
void drink(){
System.out.println("人都会喝水");
}
void eat() {
System.out.println("人都会吃饭");
}
abstract void ethnicGroup();
}
分别定义子类:
public class AmericanPeple extends AbsPeple {
@Override
void ethnicGroup() {
System.out.println("美洲人大多数是白种人");
}
}
public class AsiaPeple extends AbsPeple{
@Override
void ethnicGroup() {
System.out.println("亚洲人大多数是黄种人");
}
public static void main(String[] args) {
AsiaPeple asiaPeple = new AsiaPeple();
asiaPeple.eat();
asiaPeple.drink();
asiaPeple.ethnicGroup();
AmericanPeple americanPeple = new AmericanPeple();
americanPeple.eat();
americanPeple.drink();
americanPeple.ethnicGroup();
System.out.println(asiaPeple);
System.out.println(americanPeple);
}
}
结果打印:
人都会吃饭
人都会喝水
亚洲人大多数是黄种人
人都会吃饭
人都会喝水
美洲人大多数是白种人
com.example.exceldemo.abstact.entity.AsiaPeple@7699a589
com.example.exceldemo.abstact.entity.AmericanPeple@58372a00
缺点:一旦一个类继承了抽象类AbsPeple
,那么它就无法继承其它类。而且我们也知道,继承实现有很多问题,太多的继承会造成代码耦合性问题,不利于以后的维护和升级。抽象骨架氤氲而生。
(3)抽象骨架概念:(抽象类继承接口,不要求重写接口中缺省、抽象方法)
可以将接口和抽象类的优点结合到一起。其中,接口用来定义类型,可能还会提供一些默认的方法,而骨架实现类负责在基本接口方法之上实现其余的非基本接口方法(ethnicGroup()方法)。扩展骨架实现类可以省去实现接口需要的大部分工作。这就是模板方法【设计模式】(中级面试很多面试官喜欢问设计模式)
定义抽象骨架:
public abstract class AbsPeple implements IPeple{
@Override
public void drink() {
System.out.println("人都要喝水");
}
@Override
public void eat() {
System.out.println("人都要吃饭");
}
}
定义私有类及成员(AsiaPeple重写接口统一方法,成员变量获取单独的重写方法):
public class AsiaPeple implements IPeple {
private AsiaPepleIpml asiaPepleIpml = new AsiaPepleIpml();
private static class AsiaPepleIpml extends AbsPeple {
@Override
public void ethnicGroup() {
System.out.println("亚洲人大多数都是黄种人");
}
}
public static void main(String[] args) {
AsiaPeple asiaPeple = new AsiaPeple();
asiaPeple.eat();
asiaPeple.drink();
asiaPeple.asiaPepleIpml.ethnicGroup();
}
@Override
public void drink() {
asiaPepleIpml.drink();
}
@Override
public void eat() {
asiaPepleIpml.eat();
}
@Override
public void ethnicGroup() {
throw new UnsupportedOperationException("不重新方法");
}
}
打印结果:
人都要吃饭
人都要喝水
亚洲人大多数都是黄种人
Process finished with exit code 0
AsiaPeple类 可以实现多个接口和定义多个抽象骨架实现类,使得AsiaPeple类非常非常灵活多变。
骨架实现类的美妙在于,他们提供了抽象类的所有实现帮助,又不存在将抽象类用于类型定义时所面临的严格限制。接口本身存在的任何默认方法,对于这个类还是有帮助的。此外,仍然可以利用骨架实现来帮助实现者完成任务:实现该接口的类可以引入一个私有的、扩展了骨架实现类的内部类,并包含一个这个内部类实例,然后将对接口方法的调用转发给这个实例。这种技术被称为模拟多重继承。
因为骨架实现是为了继承而设计的,好的文档在骨架实现中是绝对必要的,无论它是由接口上的默认方法组成还是单独的抽象类。
总而言之,要定义支持实现的类型,接口通常是最佳选择 。如果导出一个不是很简单的接口,请务必考虑配合提供一个骨架实现 。在可能的情况下,应该通过接口上的默认方法来提供骨架的实现,以便该接口的所有实现者都可以使用 。即便如此,接口上的限制常会使得抽象类形式成为骨架实现的不二之选。
所有文章无条件开放,顺手点个赞不为过吧!