最近在看抽象类的时候,突然发现自己以前有个认知是错的,赶紧记下来分享。
我以前一直以为
实现接口 = 必须把接口里所有方法都实现一遍,少一个都报错。
这个认知对,因为在写SpringBoot项目的时候,项目会分cotroller层,service层,mapper层等,一般我习惯先写控制层,然后调用业务层方法,但是业务层还没有这个方法,我会在业务层的接口补上这个方法,写完时,业务层的实现类就会报红了,因为此时接口多了一个方法,实现类必须实现他,然后我开始去实现类实现这个方法,这一套流程习以为。 但是,最近刷到一个视频,解释接口是什么,有一个操作就是就是使用抽象类实现接口,可以不实现所有方法。 也就是说如果实现接口的是一个抽象类,居然可以只实现部分方法,甚至一个都不实现,编译器都不管你。
java
interface Animal {
void eat();
void sleep();
void run();
}
// 抽象类:我就实现一个,咋了?不报错
abstract class Dog implements Animal {
@Override
public void eat() {
System.out.println("吃骨头");
}
// sleep和run?我不写,留给子类去写
}
为什么可以这样?
因为抽象类本身就可以有抽象方法,接口里没实现的方法,到了抽象类里自动就变成了抽象方法,继续往下传。相当于"甩锅"给子类。
原来如此我也才恍然大悟,但是更让我意外的是,之前学习的适配器模式,既然用到了这种操作。
假设有一个音频播放器,它只能播放 MP3 文件。现在,我们需要播放 VLC 和 MP4 文件,可以通过创建一个适配器来实现,如下 已有的顶级接口:
java
public interface MediaPlayer {
void play(String audioType, String fileName);
}
已有的高级播放器接口(但不兼容):
java
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
已有的具体播放器类:
java
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 不支持,但被迫实现
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 不支持,但被迫实现
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
AudioPlayer 要实现 MediaPlayer 接口,根据传入格式决定用哪个播放器,这个过程本身没有问题,但是仔细想一想,假如这个高级播放器有十几个方法,那具体的播放器就要被迫去实现这十几个自己根本不支持的方法,这也太不简洁了。
此时此刻,前面说的这种骚操作就派上用场了使用抽象类去实习接口,具体的播放器再去继承这个抽象类,然后有选择性的重写自己需要的方法。
第一步:创建缺省适配器抽象类
java
public abstract class AbstractAdvancedMediaPlayer
implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 默认空实现
}
@Override
public void playMp4(String fileName) {
// 默认空实现
}
}
第二步:具体的播放器只重写自己需要的
java
public class VlcPlayer extends AbstractAdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
// playMp4 不写,用父类空实现
}
public class Mp4Player extends AbstractAdvancedMediaPlayer {
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
// playVlc 不写,用父类空实现
}
代码瞬间干净了!
总结 这可能是一个很基础的知识,但是平时习惯下你可能并不在意这个细节,可是它在某些地方却起着很大的作用,你不仔细想一想,还真不发现啊。