我们已经知道,Java提供了两种机制来定义允许多个实现的类型:接口和抽象类。但是这两种机制在某些情况下存在着一些我们不喜欢的地方。
比如,我们想模拟不同品牌的Android手机,需要实现一些手机的基本功能:开机、调节音量、下载App。
接口实现
我们先定义一个接口IAndroidPhone
,提供相应的接口方法:
interface IAndroidPhone {
void powerOn();
void setVolume(int volume);
void downloadApp(String appName);
}
现在,来定义一部三星手机:
class SamsungPhone implements IAndroidPhone {
@Override public void powerOn() {
System.out.println("长按电源键开机");
}
@Override public void setVolume(int volume) {
if(volume < 0){
volume = 0;
}else if(volume > 100){
volume = 100;
}
System.out.println("把音量调节为:" + volume);
}
@Override public void downloadApp(String appName) {
System.out.println("打开三星应用市场,搜索 " + appName + ",然后下载");
}
}
再定义一部Pixel手机
class PixelPhone implements IAndroidPhone {
@Override public void powerOn() {
System.out.println("长按电源键开机");
}
@Override public void setVolume(int volume) {
if(volume < 0){
volume = 0;
}else if(volume > 100){
volume = 100;
}
System.out.println("把音量调节为:" + volume);
}
@Override public void downloadApp(String appName) {
System.out.println("打开Google Play,搜索 " + appName + ",然后下载");
}
}
我们可以看到,一旦实现
IAndroidPhone
的接口,就需要实现它定义的所有方法。然而,其中某些方法是完全重复的。比如这里的powerOn()
和setVolume(int volume)
方法,每实现一个不同品牌的手机就得把这些代码重复一次。
抽象类实现
我们把上面的IAndroidPhone
接口定义成抽象类来试试,因为抽象类可以提供默认实现,所以我们定义一个抽象类AbstractAndroidPhone
如下:
abstract class AbstractAndroidPhone {
void powerOn(){
System.out.println("长按电源键开机");
}
void setVolume(int volume){
if(volume < 0){
volume = 0;
}else if(volume > 100){
volume = 100;
}
System.out.println("把音量调节为:" + volume);
}
abstract void downloadApp(String appName);
}
可以看到,抽象类AbstractAndroidPhone
提供了powerOn
和setVolume(int volume)
方法的默认实现。
那么现在重新定义手机类SamsungPhone
,让它继承AbstractAndroidPhone
,并实现和之前同样的功能,代码如下:
class SamsungPhone extends AbstractAndroidPhone {
@Override
void downloadApp(String appName) {
System.out.println("打开三星应用市场,搜索 " + appName + ",然后下载");
}
}
这样子类只需要实现或者覆盖(override)它自己需要方法,因为抽象类提供的某些方法的默认实现已经满足了子类的实现需求。这样就不用重复写大量的相同代码。
但是使用抽象类也有很大的缺点,一旦一个类继承了抽象类
AbstractAndroidPhone
,那么它就无法继承其它类。而且我们也知道,继承实现有很多问题,太多的继承会造成代码耦合性问题,不利于以后的维护和升级。
抽象骨架实现类
抽象骨架实现类的出现就是为了避免以上两种实现方式的缺点。既不用写重复的代码,又能避免抽象类的限制。
我们定义一个抽象类AbsAndroidPhone
来实现上面的IAndroidPhone
接口,并给某些方法提供默认实现,如下:
abstract class AbsAndroidPhone implements IAndroidPhone{
@Override public void powerOn() {
System.out.println("长按电源键开机");
}
@Override public void setVolume(int volume) {
if(volume < 0){
volume = 0;
}else if(volume > 100){
volume = 100;
}
System.out.println("把音量调节为" + volume);
}
}
现在重写之前的SamsungPhone
类:
class SamsungPhone implements IAndroidPhone {
private SamsungPhoneImp samsungImp = new SamsungPhoneImp();
@Override public void powerOn() {
samsungImp.powerOn();
}
@Override public void setVolume(int volume) {
samsungImp.setVolume(volume);
}
@Override public void downloadApp(String appName) {
samsungImp.downloadApp(appName);
}
private static class SamsungPhoneImp extends AbsAndroidPhone{
@Override void downloadApp(String appName) {
System.out.println("打开三星应用市场,搜索 " + appName + ",然后下载");
}
}
}
在子类SamsungPhone
中定义了一个私有类SamsungPhoneImp
来实现抽象骨架实现类AbsAndroidPhone
,并把接口方法的实现转发给该私有类的实例。
这样既避免了在子类中重复写大量相同的代码,又不会限制子类继承其它类,同时子类也可以根据需要实现其它接口。