先扯两句

  常常责怪自己,当初不应该。想写《设计模式》就好好写不好吗,非要搞什么拓展,在“工程模式”要介绍什么是泛型;结果泛型说到泛型接口,又想要再介绍介绍什么是接口,写个博客,咋就也搞成面向对象了,各种封装啊!刚刚写完类和抽象类,终于到接口了,快点说完,就可以接着聊泛型了。

  好吧,这篇博客只是自己的学习笔记罢了,算是对于工作以来遇到使用接口的地方进行一个归纳,肯定没有那些大神整理的专业,有逻辑性,如果大家有兴趣的话,闲来无事的话,就姑且来看看吧。

正文

  上一篇写抽象类的时候,给自己写了个完全不符合自己心意的女朋友,不过好消息是,那个完全不符合自己心意的女孩并没有在现实中出现,而坏消息则是,老头子我依然单身。

  为了让自己摆脱单身的状态,痛定思痛下,终于下定决心,要提升自己的能力,让自己更强了才能吸引女孩(虽然不想承认,强不强的不知道,但确实是变秃了好多)。那就让接口来帮我提升一下自己的能力吧!不过让接口帮忙,先看看接口是什么?

  1. 在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
  2. 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
  3. 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
  4. 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

  好吧,以上就是菜鸟教程的Java 接口,大家如果对于一些专业的定义啊、范例啊之类的有兴趣,可以自己去看看。这部分内容让我说,说得不如人家精辟,摘抄下来又太占篇幅,尤其是我这种看定义就想睡觉的人,改排版都能改睡着了,所以这里就不赘述了。

  还是老样子,非要强行解释一波的话也可以:接口就是相关联的抽象方法的集合,用于专人干专事。抽象方法不知道的可以看上一篇博客胖菜鸟说抽象类中的相关介绍(当然,也没有专业的定义)

  不管听没听懂,我还是先练练技能找女朋友,这才是正事.想要留住一个女孩的心,要先留住女孩的胃(别问我这句话为不是男人了,我都姜太公找女朋友这么多年了,到现在也没见谁来留住我的胃啊),那就从做一道清蒸多宝鱼开始吧:

  无论做什么菜,肯定有其相关的一些信息,例如:菜名、所需食材、烹饪流程,当然除了这些意外,对老头子我来说,学习做菜还多出了一个目标,因此针对这些相关的内容,我们就可以封装一个菜肴的接口了:

/*** 菜肴接口*/
public interface IDish {/*** 目标*/String aim = "想要留住一个女孩的心,要先留住女孩的胃";/*** 获取菜肴的名字** @return 菜肴的名字*/String getDishName();/*** 购买食材** @param ingredients 食材列表*/void buyIngredients(String... ingredients);/*** 烹饪*/void cook();
}

包含

  既然已经有了一个接口,那么我们就先来看一下一个接口都能包含哪些内容吧。

接口的声明与类声明的区别

  可以看到,接口这货的声明虽然与类的声明挺像的,但还是有些细微的区别,

  1. 是用class修饰的,而接口是用interface修饰的。
  2. 接口不 添加final修饰符(接口创建的目的就是用来被继承的,所以不能绝育)

    而类可以添加final禁止这个类被禁止(抽象类也不能添加final,原因与接口一样)
  3. 接口不 添加abstract修饰符(接口都是抽象的,而且其中的方法也只能是抽象方法,因此添加不添加abstract都一样)

    同样,正是因为接口必须是abstract的,因为冲突才不能被final修饰

抽象方法

  抽象方法这里与抽象类相同,都是用来给实现(类叫继承,接口叫实现)它的类来实现具体方法的,说起来可能有些绕口,大家可以从***********看一下,也可以自己写一遍打码,IDE就会给出提示的。

  当然这里之所以拿出来说,不是为了让大家去看我的抽象类,而是为了说明一下与抽象类中方法使用的不同,那就是接口中必须使用抽象方法,而不能有具体的实现,不然就会报错。

  而且这还不算完,不仅必须是抽象方法,而且还必须是由public(全局可调用)来修饰,而不能使用protected(子类中可调用)或者private(只有当前方法中可以调用)

  而在类中的default(同一个包内可以调用)这种情况在接口中也是不存在的,因为当我们不填写的时候会默认为public,因此在填写public的时候,也会提示是冗余的。

属性

  哈哈,看到属性两个字的时候聪明的你一定想到了,接口的属性与类的属性肯定也不一样,但是具体区别在哪里呢?

  1. 属性默认是共有的:因此添加public会提示冗余,添加private或者protected会报错

  1. 属性默认是不可修改的:因此添加final会提示冗余,不赋初值会报错

  1. 属性默认是静态的,因此不需要构造接口的实现子类,就可以直接通过接口名调用

/** 清蒸多宝鱼** @author 半寿翁* @date 2020-4-5*/
public class SteamedTurbot implements IDish {……
}public class Blog {@Testpublic void studyCook() {String aim = IDish.aim;String aim2 = SteamedTurbot.aim;String aim3 = new SteamedTurbot().aim;String aim4 = Different.aim;String aim5 = new Different().aim;}
}class Different {static final String aim = ":::";
}

  可以看到,接口中的属性,无论是通过接口自身调用、通过子类调用、还是子类的实现调用都是可以的。但是我这里做了一个类与接口的对比,还是发现了在调用中警告的显示有些不同,代码可能看不出来,还是给大家截图看一下吧:

  可以看到实体类中的静态方法,如果实现后再去调用的话,会出现警告信息“static修饰的方法或者属性,建议通过实例类调用,而不是通过一个类的实现来调用”。但是接口中的方法根据前面的分析,也是静态的,但是在new SteamedTurbot().aim;却没有提示相应的警告信,这里只能分析是IED的问题,毕竟在接口中的静态属性的static字段会被认定为冗余字段自动忽略,如果IED是依据这个字段来判断的自然无法识别(注意自动忽略四个字,也就是你写不写IDE都看不到)。

  不过这里只是报的警告,也就是说即便你在编程的时候一定要这么用,也是可以的。如果要说弊端的话,也就是后续其他同事维护的时候,可能会将这个静态属性误认为为普通属性。

  如果还有其他疑问吗,欢迎大家在评论区一起讨论。

默认方法

  好吧,这一段是我后补进来的,之前在写的接口的时候资料查的还是不完整,遗漏了这个部分,资料来源是RUNOOB-Java 8 默认方法,大家如果想学习一门语言的基础知识,或者是一些新的调整,也可以到这个网站多逛逛,上面给的是默认方法的链接地址。

  Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法,我们只需在方法名前面加个 default 关键字即可实现默认方法。

  其主要的作用就是在接口中可以做一些方法实现,说白了就是把接口搞成了抽象方法,若让我个人强行解释这样比抽象方法有什么好处,那就一个类可以实现多个接口,却只能继承一个方法。让这种实现更灵活一些。

  不过官方之所以是官方,就是因为人家理解更加深刻,更加官方:

  首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

  由于Java 8开始就收费了,作为一个个人小菜鸟,实在花不起这个钱,所以这里就没办法写代码测试了,只能是请大家移步到RUNOOB-Java 8 默认方法中看具体的实例了。作为一个Android开发的穷人,后续只能转kotlin了,希望大家谅解

应用场景

  优先说明,由于我是开发android的,所以对于一些后台中的接口具体使用场景也不是特别熟悉,只能从我个人的工作中遇到的情况进行分析,可能有一些举例并不一定在java后台一样适用,或者有一些在java后台开发中的常用场景,在我这里找不到对应的体现,我尽可能的将遇到的场景多列举一些,其他的部分只能大家根据自己的情况补充了。

相关属性集合

  这个在前面的接口例子实际上就是这么封装的,也就是将做菜的几个环节封装到一起,然后由具体的实现类去实现这个接口,并完善每一个环节的功能,比如清蒸多宝鱼类(SteamedTurbot)就是菜肴(IDish)接口的具体实现:

/*** 菜肴接口*/
public interface IDish {/*** 目标*/String aim = "想要留住一个女孩的心,要先留住女孩的胃";/*** 获取菜肴的名字** @return 菜肴的名字*/String getDishName();/*** 购买食材** @param ingredients 食材列表*/void buyIngredients(String... ingredients);/*** 烹饪*/void cook();
}/** 清蒸多宝鱼** @author 半寿翁* @date 2020-4-5*/
public class SteamedTurbot implements IDish {private List<String> ingredientList = new ArrayList<>();@Overridepublic String getDishName() {return "清蒸多宝鱼";}/*** 购买食材:* <p>* - 多宝鱼* - 葱* - 姜* - 红辣椒* - 花椒* - 油* - 料酒* - 蒸鱼豉油。** @param ingredients 食材列表*/@Overridepublic void buyIngredients(String... ingredients) {ingredientList.addAll(Arrays.asList(ingredients));}/*** 烹饪* <p>* 第1步、买到一条鲜活的多宝鱼摘除鱼鳃和内脏清洗干净沥干水份备用* 第2步、葱切丝和姜切片好备用* 第3步、多宝鱼的两面用刀割几刀但不要割透,淋上料酒去腥* 第4步、上下两面放上葱姜煨制20分钟入味* 第5步、盘中重新摆入葱和姜备用* 第6步、多宝鱼摆入盘中上面再摆一些葱姜放入蒸锅大火蒸15分钟* 第7步、出锅,在鱼身上撒上葱丝、红辣椒丝* 第8步、均匀的淋上蒸鱼豉油* 第9步、锅中倒入适量油加热放入花椒炸香切记不要炸糊,炸好后取出花椒* 第10步、重新切葱花和红辣椒放在多宝鱼身上再浇上热气腾腾的花椒油瞬间满厨房爆香*/@Overridepublic void cook() {}
}

  这是最最通用的一种接口的使用形式,但是说实话,作为一个Android程序猿,实际项目中很少用到这种封装形式,实在是工程太小了,除非是封装通用系统架构的,但是后台的使用相对就要多很多,前两天玩了一下Spring Boot,数据库读写都是这种形式的。

@Test
public void studyCook() {SteamedTurbot steamedTurbot = new SteamedTurbot();steamedTurbot.buyIngredients("多宝鱼", "葱", "姜", "红辣椒", "花椒", "油", "料酒", "蒸鱼豉油");steamedTurbot.cook();
}

  而当我们使用的时候,只需要构造对应的实现类,然后调用相应的方法就可以了。但是可以看到我们的cook方法中现在还没有编写任何的内容,而且做菜的时候,肯定不能菜自己把自己给做了啊,我们肯定要有一些操作的部分,毕竟可以看到,整个烹饪过程一共有十步,还是挺多流程的,那该怎么操作呢?这里就需要接口的第二个应用场景了。

规划执行流程

  上面我们买食材的时候,虽然是按照顺序列举的,但是实际上,我们去菜市场的时候肯定不会一定要按照这个顺序去买,而是哪个近,哪样更好(没办法,别忘了我们的aim,不能图省钱啊)我才会去哪买,哪怕我倒着买,也不会影响到最后清蒸多宝鱼做出来的味道。

steamedTurbot.buyIngredients("蒸鱼豉油", "料酒", "油", "花椒", "红辣椒", "姜", "葱", "多宝鱼"
);

  但是制作的流程肯定不能这样,如果倒着做出来,你不是研发了一个新的菜系,就是标准的黑暗料理了,所以这里只能通过一个接口来规划一下(这里也是相关属性的集合,只是换了一种应用场景)(完整代码见附录):

/** 制作清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public interface ICookSteamedTurbot {/*** 清洗、摘除内脏** @param turbot 多宝鱼* @return 清理好的多宝鱼*/String cleanTurbot(String turbot);/*** 切葱丝、姜片** @param turbot   多宝鱼* @param scallion 葱丝* @param ginger   姜片*/void cutScallionAndGinger(String turbot, String scallion, String ginger);/*** 多宝鱼切花刀,加料酒** @param turbot      多宝鱼* @param cookingWine 料酒*/void cutTurbotAndCookingWine(String turbot, String cookingWine);/*** 腌制** @param turbot   多宝鱼* @param callBack 时间计时器回调*/void pickling(String turbot, TimeUpCallBack callBack);...
}

  有了流程的接口,下面就看我们怎么进行流程规划了:

/** 清蒸多宝鱼** @author 半寿翁* @date 2020-4-5*/
public class SteamedTurbot implements IDish {private ICookSteamedTurbot iCookSteamedTurbot;public SteamedTurbot(ICookSteamedTurbot iCookSteamedTurbot) {this.iCookSteamedTurbot = iCookSteamedTurbot;}/*** 烹饪* <p>* 第1步、买到一条鲜活的多宝鱼摘除鱼鳃和内脏清洗干净沥干水份备用* 第2步、葱切丝和姜切片好备用* 第3步、多宝鱼的两面用刀割几刀但不要割透,淋上料酒去腥* 第4步、上下两面放上葱姜煨制20分钟入味* 第5步、盘中重新摆入葱和姜备用* 第6步、多宝鱼摆入盘中上面再摆一些葱姜放入蒸锅大火蒸15分钟* 第7步、出锅,在鱼身上撒上葱丝、红辣椒丝* 第8步、均匀的淋上蒸鱼豉油* 第9步、锅中倒入适量油加热放入花椒炸香切记不要炸糊,炸好后取出花椒* 第10步、重新切葱花和红辣椒放在多宝鱼身上再浇上热气腾腾的花椒油瞬间满厨房爆香*/@Overridepublic String cook() {if (null != iCookSteamedTurbot) {final String[] currentTurbot = {"多宝鱼"};currentTurbot[0] = iCookSteamedTurbot.cleanTurbot(currentTurbot[0]);currentTurbot[0] = iCookSteamedTurbot.cutScallionAndGinger(currentTurbot[0], "葱丝", "姜");currentTurbot[0] = iCookSteamedTurbot.cutTurbotAndCookingWine(currentTurbot[0], "料酒");iCookSteamedTurbot.pickling(currentTurbot[0], new TimeUpCallBack() {/*** 等待腌制的时间* @param turbot 腌制好多宝鱼*/@Overridepublic void onTimeUp(String turbot) {currentTurbot[0] = iCookSteamedTurbot.platePresentation(turbot, "葱", "姜");iCookSteamedTurbot.steam(currentTurbot[0], new TimeUpCallBack() {/*** 等待蒸的时间* @param turbot 蒸熟的多宝鱼*/@Overridepublic void onTimeUp(String turbot) {currentTurbot[0] = iCookSteamedTurbot.scatterScallionPepper(turbot);currentTurbot[0] = iCookSteamedTurbot.pourFishAndSoySauce(currentTurbot[0]);currentTurbot[0] = iCookSteamedTurbot.fryingOil(currentTurbot[0]);currentTurbot[0] = iCookSteamedTurbot.coverScallionPepperAgain(currentTurbot[0]);}});}});return currentTurbot[0];} else {return null;}}
}

  这样我们就规划好了烹饪的流程,同时也对外提供了用来实现对应流程的接口入口,下面只需要在外面实现,就可以了(完整代码见附录)。

@Test
public void studyCook() {SteamedTurbot steamedTurbot = new SteamedTurbot(iCookSteamedTurbot);steamedTurbot.buyIngredients("多宝鱼", "葱", "姜", "红辣椒", "花椒", "油", "料酒", "蒸鱼豉油");String finishTurbot = steamedTurbot.cook();if (null != finishTurbot) {System.out.println(MessageFormat.format("美味的**{0}**可以吃了", finishTurbot));} else {System.out.println("我的鱼呢");}
}/*** 流程实现*/
private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {private String currentTurbot;/*** 清理多宝鱼** @param turbot 多宝鱼* @return 清理好的多宝鱼*/@Overridepublic String cleanTurbot(String turbot) {if (null != turbot) {currentTurbot = MessageFormat.format("{0}{1}", "清洁完成的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("没有多宝鱼");}return currentTurbot;}/*** 在多宝鱼下方葱丝与姜片** @param turbot   多宝鱼* @param scallion 葱丝* @param ginger   姜片*/...
};

  由于例子中使用的食材是String,如果大家想要试验购买食材缺少的情况,可以调整一下框架,这里就不做演示了。而且肯定还有更复杂的逻辑使用,就看大家后面应用的时候自己去验证了,这里就继续第三个应用的场景了。

回调(CallBack)

  可以看到其他的流程都是直接依照顺序写的,但是其中有这么两个流程长得跟其他的流程不一样:

/** 制作清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public interface ICookSteamedTurbot {.../*** 腌制** @param turbot   多宝鱼* @param callBack 时间计时器回调*/void pickling(String turbot, TimeUpCallBack callBack);.../*** 蒸** @param turbot   多宝鱼* @param callBack 时间计时器回调*/void steam(String turbot, TimeUpCallBack callBack);...
}

  腌制多宝鱼以及蒸多宝鱼,这两个流程与其他的流程不同的是,其他无论是清洗多宝鱼、处理配料都需要一直操作,而腌制和蒸都需要有一个等待的时间,这个时间我们完全可以刷一集动漫或者撸一局农药(毕竟只有15~20分钟,也干不了太多事),而作为脑子不太好用的我,一旦离开了厨房,下次想起来就不一定是什么时候了。

  因此就需要设置一个闹铃,到时间以后会告诉我们别玩了,时间到了,接口同样可以实现这个功能,就是我们的回调接口,通常来说,回到接口在android中使用的环境是多线程操作的时候,等待子线程执行完时获取回调。在我们的例子中就是等待腌制或者蒸的时间,那我们调整一下实现的方法(偷个懒,用sleep的毫秒代替分钟了,不然真的等不起啊):

    private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {.../*** 腌制** @param turbot   多宝鱼* @param callBack 时间计时器回调*/@Overridepublic void pickling(final String turbot, final TimeUpCallBack callBack) {if (!isNull(turbot, callBack)) {final long beginTime = System.currentTimeMillis();System.out.println(MessageFormat.format("开始腌制,时间:{0}", beginTime));new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(20);long endTime = System.currentTimeMillis();currentTurbot = MessageFormat.format("{0}{1}", "腌制好的 ", turbot);System.out.println(MessageFormat.format("结束腌制,时间:{0},共耗时{1}", endTime, endTime - beginTime));System.out.println(currentTurbot);callBack.onTimeUp(null);} catch (InterruptedException e) {e.printStackTrace();}}}).start();} else {System.out.println("调料不全");callBack.onTimeUp(null);}}.../*** 蒸** @param turbot   多宝鱼* @param callBack 时间计时器回调*/@Overridepublic void steam(final String turbot, final TimeUpCallBack callBack) {if (!isNull(turbot, callBack)) {final long beginTime = System.currentTimeMillis();System.out.println(MessageFormat.format("开始腌制,时间:{0}", beginTime));new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(15);long endTime = System.currentTimeMillis();currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);System.out.println(MessageFormat.format("结束腌制,时间:{0},共耗时{1}", endTime, endTime - beginTime));System.out.println(currentTurbot);callBack.onTimeUp(null);} catch (InterruptedException e) {e.printStackTrace();}}}).start();} else {System.out.println("调料不全");callBack.onTimeUp(null);}}...}

  运行后的结果如下:

  当然,打印的时间还是差了几毫秒的,但是毫秒级的程序运行大家理解一下吧,如果谁有兴趣试一下分钟的就不会出现这种尴尬了,不过这里可没有完成后的打印,因为是走的子线程,所有直接从cook回调只能得到进入子线程前的结果,如果想要获得烹饪好的清蒸多宝鱼,就只能调整IDisk的cook()方法,添加一个烹饪结束的回调,或者其他什么地方添加一个回调就可以。

监听者(Listener)

  上面说了CallBack,其实我们的Listener与回调的原理差不多,也是需要外部出发的时候执行里面的操作,但是从我这里理解,Listener与CallBack的区别就是:

  • CallBack是流程的延续:在流程执行中,需要等待其中一个过程的结果才能继续向下进行
  • Listener是开启新的流程:由外部的一个事件触发这个监听者,由监听者开启一个全新的流程

  举例说明就是,在方法我们有一个步骤是蒸鱼,蒸鱼肯定就需要用锅,而就锅本身而言,它的开启关闭与蒸鱼的流程并不是强相关的。我可以在做蒸鱼之前蒸了饭就开启了蒸锅,也可能蒸好鱼之后忘记了关掉蒸锅,直接进行后续的操作去了。因此,锅的开启和关闭就可以使用Listener来实现调用。

/** 蒸锅使用状态变更监听者** @author 半寿翁* @date 2020-4-6*/
public interface ISteamerStateChangeListener {/*** 设置定时时间的开启** @param time       定时时间* @param ingredient 食材*/void open(Object ingredient, long time);/*** 无定时的开始,需手动关闭** @param ingredient 食材*/void open(Object ingredient);/*** 手动关闭*/void close();
}/** 蒸锅** @author 半寿翁* @date 2020-4-6*/
public class Steamer implements ISteamerStateChangeListener {private Object ingredient;/*** 定时器线程池*/private ScheduledExecutorService service;/*** 记过回调*/private SteamDoneCallBack callBack;/*** 线程回调*/private Runnable runnable = new Runnable() {@Overridepublic void run() {callBack.done(ingredient);}};/*** 构造方法** @param callBack 结果回调*/public Steamer(SteamDoneCallBack callBack) {this.callBack = callBack;}@Overridepublic void open(Object ingredient, long time) {if (null == service) {service = Executors.newSingleThreadScheduledExecutor();}if (service.isShutdown()) {service.schedule(runnable, time, TimeUnit.MILLISECONDS);} else {throw new IllegalStateException("锅正用着呢,后面等着!!!");}}@Overridepublic void close() {if (null != service) {service.shutdown();} else {throw new IllegalStateException("锅都没打开,关什么关!!!");}}/*** 蒸完的回调*/public interface SteamDoneCallBack {/*** 结果回传** @param ingredient 蒸好的食材*/void done(Object ingredient);}
}

  蒸锅调整好了,让我们调整一下蒸锅的流程,然后蒸一下鱼试试效果:

/*** 蒸** @param turbot   多宝鱼* @param callBack 时间计时器回调*/
@Override
public void steam(final String turbot, final TimeUpCallBack callBack) {if (!isNull(turbot, callBack)) {final long beginTime = System.currentTimeMillis();System.out.println(MessageFormat.format("开始蒸,时间:{0}", beginTime));new Steamer(new Steamer.SteamDoneCallBack() {@Overridepublic void done(Object ingredient) {long endTime = System.currentTimeMillis();currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);System.out.println(MessageFormat.format("结束蒸,时间:{0},共耗时{1}", endTime, endTime - beginTime));System.out.println(currentTurbot);callBack.onTimeUp(currentTurbot);}}).open(turbot, 15);} else {System.out.println("调料不全");callBack.onTimeUp(null);}
}

  可以看到我们的鱼也成功蒸好,不过好像一不小心测出来了线程池在毫秒级比直接new Thread要慢一些,可能是我使用错了吧,如果有大神发现了能够指点一下。

类型判断

  好了经过前面一系列的操作,我们终于把清蒸多宝鱼做好了,下面就看能不能靠着这道菜找到个女朋友了。如果出现喜欢吃清蒸多宝鱼的还有机会;如果遇到个讨厌清蒸多宝鱼的,那就肯定没戏了。

/** 女孩接口** @author 半寿翁* @date 2020-4-6*/
public interface IGirl {
}/** 喜欢吃清蒸多宝鱼的女孩** @author 半寿翁* @date 2020-4-6*/
public interface ILikeSteamedTurbot extends IGirl {
}/** 不喜欢吃清蒸多宝鱼的女孩** @author 半寿翁* @date 2020-4-6*/
public interface IDislikeSteamedTurbot extends IGirl {
}/** Lina:不喜欢吃清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public class Lina implements IDislikeSteamedTurbot {
}/** Tina:不喜欢吃清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public class Tina implements IDislikeSteamedTurbot {
}/** Shawn:喜欢吃清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public class Shawn implements ILikeSteamedTurbot {
}/*__________________________执行验证__________________________*/@Test
public void blindDate() {judge(new Lina(), new Tina(), new Shawn());
}
private void judge(IGirl... girls) {for (IGirl girl : girls) {if (girl instanceof ILikeSteamedTurbot) {System.out.println(MessageFormat.format("{0}喜欢吃清蒸多宝鱼,我要追她", girl.getClass().getSimpleName()));} else if (girl instanceof IDislikeSteamedTurbot) {System.out.println(MessageFormat.format("{0}讨厌吃清蒸多宝鱼,没机会了", girl.getClass().getSimpleName()));}}
}

  用这种方式,可以依据我们的需求,区分出来不同的类,然后对不同类型的类的进行相应的业务逻辑处理。

  暂时能想到的接口的应用就是上面这几类,实际上有几类相互之间长得都差不多,只是使用的业务场景有所不同,而调整的不同的命名方式,里面有没有不合理的地方,或者缺少的地方,也欢迎大家在评论区指教。

鸣谢

  • 哭的像个孩子:图片取自 Pixabay 的 joffi
  • 清蒸多宝鱼菜谱:清蒸多宝鱼,鲜嫩营养,方便快捷又好吃到爆
  • 遗忘的代价:图片取自 Pixabay 的 David Mark

附录:

ICookSteamedTurbot

/** 制作清蒸多宝鱼** @author 半寿翁* @date 2020-4-6*/
public interface ICookSteamedTurbot {/*** 清洗、摘除内脏** @param turbot 多宝鱼* @return 清理好的多宝鱼*/String cleanTurbot(String turbot);/*** 切葱丝、姜片** @param turbot   多宝鱼* @param scallion 葱丝* @param ginger   姜片*/String cutScallionAndGinger(String turbot, String scallion, String ginger);/*** 多宝鱼切花刀,加料酒** @param turbot      多宝鱼* @param cookingWine 料酒*/String cutTurbotAndCookingWine(String turbot, String cookingWine);/*** 腌制** @param turbot   多宝鱼* @param callBack 时间计时器回调*/void pickling(String turbot, TimeUpCallBack callBack);/*** 摆盘** @param turbot   多宝鱼* @param scallion 葱丝* @param ginger   姜片*/String platePresentation(String turbot, String scallion, String ginger);/*** 蒸** @param turbot   多宝鱼* @param callBack 时间计时器回调*/void steam(String turbot, TimeUpCallBack callBack);/*** 撒葱丝、辣椒** @param turbot 多宝鱼*/String scatterScallionPepper(String turbot);/*** 淋蒸鱼豉油** @param turbot 多宝鱼*/String pourFishAndSoySauce(String turbot);/*** 炸花椒爆香油** @param turbot 多宝鱼*/String fryingOil(String turbot);/*** 重新撒上TimeUpCallBack** @param turbot 多宝鱼*/String coverScallionPepperAgain(String turbot);
}

ICookSteamedTurbot实现

public class Blog {@Testpublic void studyCook() {SteamedTurbot steamedTurbot = new SteamedTurbot(iCookSteamedTurbot);steamedTurbot.buyIngredients("多宝鱼", "葱", "姜", "红辣椒", "花椒", "油", "料酒", "蒸鱼豉油");String finishTurbot = steamedTurbot.cook();if (null != finishTurbot) {System.out.println(MessageFormat.format("美味的**{0}**可以吃了", finishTurbot));} else {System.out.println("我的鱼呢");}}private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {private String currentTurbot;/*** 清理多宝鱼** @param turbot 多宝鱼* @return 清理好的多宝鱼*/@Overridepublic String cleanTurbot(String turbot) {if (null != turbot) {currentTurbot = MessageFormat.format("{0}{1}", "清洁完成的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("没有多宝鱼");}return currentTurbot;}/*** 在多宝鱼下方葱丝与姜片** @param turbot   多宝鱼* @param scallion 葱丝* @param ginger   姜片*/@Overridepublic String cutScallionAndGinger(String turbot, String scallion, String ginger) {if (!isNull(turbot, scallion, ginger)) {currentTurbot = MessageFormat.format("{0}{1}", "添加好葱丝与姜片的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 料酒去腥** @param turbot      多宝鱼* @param cookingWine 料酒*/@Overridepublic String cutTurbotAndCookingWine(String turbot, String cookingWine) {if (!isNull(turbot, cookingWine)) {currentTurbot = MessageFormat.format("{0}{1}", "料酒去腥的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 腌制** @param turbot   多宝鱼* @param callBack 时间计时器回调*/@Overridepublic void pickling(String turbot, TimeUpCallBack callBack) {if (!isNull(turbot, callBack)) {currentTurbot = MessageFormat.format("{0}{1}", "腌制好的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}callBack.onTimeUp(currentTurbot);}/*** 葱姜摆盘** @param turbot 多宝鱼* @param scallion 葱丝* @param ginger   姜片* @return 摆好盘*/@Overridepublic String platePresentation(String turbot, String scallion, String ginger) {if (!isNull(turbot, scallion, ginger)) {currentTurbot = MessageFormat.format("{0}{1}", "葱姜摆好盘的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 蒸** @param turbot   多宝鱼* @param callBack 时间计时器回调*/@Overridepublic void steam(String turbot, TimeUpCallBack callBack) {if (!isNull(turbot, callBack)) {currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}callBack.onTimeUp(currentTurbot);}/*** 撒葱丝辣酱* @param turbot 多宝鱼* @return 撒好的鱼*/@Overridepublic String scatterScallionPepper(String turbot) {if (!isNull(turbot)) {currentTurbot = MessageFormat.format("{0}{1}", "撒好葱丝辣椒 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 淋蒸鱼豉油* @param turbot 多宝鱼* @return 淋好的多宝鱼*/@Overridepublic String pourFishAndSoySauce(String turbot) {if (!isNull(turbot)) {currentTurbot = MessageFormat.format("{0}{1}", "淋好蒸鱼豉油的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 炸花椒油,并淋到鱼身上* @param turbot 多宝鱼* @return 淋了新炸花椒油的多宝鱼*/@Overridepublic String fryingOil(String turbot) {if (!isNull(turbot)) {currentTurbot = MessageFormat.format("{0}{1}", "淋好新炸花椒油的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 重新撒葱丝辣椒* @param turbot 多宝鱼* @return*/@Overridepublic String coverScallionPepperAgain(String turbot) {if (!isNull(turbot)) {currentTurbot = MessageFormat.format("{0}{1}", "重新撒上葱丝辣椒的 ", turbot);System.out.println(currentTurbot);} else {System.out.println("调料不全");}return currentTurbot;}/*** 判断是否为空** @param objects 被判断对象* @return 是否为空*/private boolean isNull(Object... objects) {for (Object o : objects) {if (null == o) {return true;}}return false;}};
}

《java基础》-胖菜鸟说接口相关推荐

  1. 初学Java基础学习——抽象类和接口的区别

    初学Java基础学习--抽象类和接口的区别 一.关键字 1)抽象类的关键字 abstract class A{//定义一个抽象类: } class Test extends A{//子类使用exten ...

  2. java基础之抽象类和接口

    一.抽象: **抽象类(abstract class):**使用了abstract关键字所修饰的类叫做抽象类.抽象类无法实例化,也就是说,不能new出来一个抽象类的对象(实例). **抽象方法(abs ...

  3. 3、java基础:抽象类与接口的区别

    抽象类 我们都知道在面向对象的领域一切都是对象,同时所有的对象都是通过类来描述的,但是并不是所有的类都是来描述对象的.如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,那么这样 ...

  4. 每日学习-Java基础(十)接口和继承10(内部类)

    一.内部类 内部类 // 内部类有四种类型 // 1-非静态内部类 // 2-静态内部类 // 3-匿名类 // 4-本地类 1.非静态内部类 package ia10_innerClass;publ ...

  5. 黑马程序员——java基础之抽象与接口

    --- android培训.java培训.期待与您交流! ---- "动物" abstract:抽象.可以用来修饰类.修饰类之后这个类就是"抽象类" 修饰类之后 ...

  6. Java基础总结篇---Map接口

    Map 将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值.除此之外你得明确的是Map是一个接口你得使用实现Map的类来实例化.其次是Map中数据存储格式是K-V键值对方法存储. ...

  7. Java基础_面向对象之接口

    一.接口的概念: 接口是功能的集合,其是一个比抽象类还抽象的类,也可以把它看作是一种数据类型. 接口只是描述其具备的方法,但是并未具体实现,具体的实现是由接口的实现类去完成. 二.接口的定义: 接口的 ...

  8. Java基础笔记:Day_10 接口、匿名内部类

    一.接口 定义类语法: [修饰符] class [类名] {} 定义接口语法:[修饰符] interface [接口名] {} 在这里我们还没有考虑接口的父接口! 接口命名问题:习惯以able结尾以表 ...

  9. java基础_static关键字,接口,内部类,匿名内部类

    关键字static 当我们编写一个类时,其实就是在描述其对象的属性和行为,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有时候希望无论是否产生了对象或 ...

最新文章

  1. Ubuntu 16.04安装双显卡驱动方法收集
  2. 初学者如何学习机器学习中的L1和L2正则化
  3. 谷歌最新财报:平均每天入账5个亿,还是不及预期;皮猜:未来靠云+AI
  4. 单源最短路——dijkstra算法
  5. vue-resource使用
  6. 关于同时可用git命令clone和TortoiseGit拉取代码不需要密码
  7. 2021最新基于uniapp的计算机考研助手小程序(含管理端)
  8. 2017中国大学生程序设计竞赛 - 女生专场C【前后缀GCD】
  9. SQlite数据库的C编程接口(三) 预处理语句(Prepared Statements) ——《Using SQlite》读书笔记 .
  10. 福建省高考成绩查询2021具体时间,福建高考时间2021具体时间表一览 福建高考时间是几月几号...
  11. 胖哈勃杯Pwn400、Pwn500详解
  12. 基于 CIM 的智慧社区总体框架
  13. 报错 No active profile set, falling back to default profiles: default 解决
  14. OpenHarmony 3.2 Release HDF的IDL文件初探(上)
  15. 连载《阿里巴巴·马云》4 : 他就像一个鬼鬼祟祟的坏人
  16. Ruby-学习之路1.1
  17. 2013年春节前订票经历及经验分享
  18. GitHub标星90K,这份持续霸榜的Leetcode刷题手册到底有多强?
  19. 最好用的股市技术指标
  20. 联想笔记本更换固态硬盘和重装系统

热门文章

  1. 教大家了解数显充气泵传感器,也就是气压传感器在充气泵中的应用
  2. c语言程序水准路线,C++语言在水准路线简易平差中的应用实例(17页)-原创力文档...
  3. CFD-Post后处理,你真的会做吗?
  4. 使用 JADE 平台进行智能体开发
  5. 非期望产出超效率SBM模型MATLAB代码
  6. 图像 引言 深度学习_用树莓派4b构建深度学习应用(十二)口罩篇
  7. 安化云台山风景区超详细旅行计划
  8. python爬取歌词生成词云图_爬取毛不易歌词作词云展示
  9. Scratch少儿编程案例-植物大战僵尸完整版
  10. GITLAB 下载指定 commit 的代码仓库