原来我一直理解错了:实现接口真的必须实现所有方法吗?

最近在看抽象类的时候,突然发现自己以前有个认知是错的,赶紧记下来分享。

我以前一直以为

实现接口 = 必须把接口里所有方法都实现一遍,少一个都报错。

这个认知对,因为在写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 不写,用父类空实现
}

代码瞬间干净了!

总结 这可能是一个很基础的知识,但是平时习惯下你可能并不在意这个细节,可是它在某些地方却起着很大的作用,你不仔细想一想,还真不发现啊。

相关推荐
小羊没烦恼!4 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里4 天前
java中的继承和多态的区别
java
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕4 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖4 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时4 天前
01-06-A-JVM排查实战详解
java·jvm
vipxieliang4 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi4 天前
mysql 查询所有表名并授权
java
ProcessOn官方账号4 天前
java函数式编程--入门基础
java·编程·函数式编程