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

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

我以前一直以为

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

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

代码瞬间干净了!

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

相关推荐
万年咸鱼1 小时前
Java BufferedOutputStream 详解:原理、用法与性能优化
java·开发语言·性能优化
万年咸鱼1 小时前
Java BufferedInputStream 详解:原理、用法与实战
java·开发语言·python
~木雨1 小时前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
艾莉丝努力练剑2 小时前
【AI大模型接入SDK】Gemini模型接入知识体系
java·开发语言·网络·人工智能·网络协议·学习·http
云运维笔记2 小时前
Zabbix 分布式监控搭建实战:基于 Proxy 实现 MySQL、Java、Nginx 监控
java·mysql·zabbix
zhanghaha13142 小时前
Python进阶教程:28_queue 队列模块 零基础超详细教程
java·开发语言·python
司小豆2 小时前
第七课:DeepSeek Harness 服务与依赖注入
java·服务器·开发语言·github·ai编程
许彰午2 小时前
24-MyBatisHelper与autoCount
java·低代码·架构
CRMEB系统商城2 小时前
汽车养护连锁的数字化底座CRMEB 多门店系统实战方案
java·开发语言·小程序·开源·汽车