“小白啊,小A刚来,你要多带带他。这个项目是你一手做起来的,这一点上我很赞同你的想法。你可以做接口设计,然后把具体的实现交给小A去做。”
“接口啊,大学里面好像有教过。”
“是啊,接口是一种特殊的类,里面只有方法的声明而没有方法的实现。不过最新的jdk也允许写方法体了。”
“嗯嗯,我知道的,可是上学那会我就特别不理解接口到底有啥用,不写方法实现,就是一个空壳子哇!”
“嗯,接口的作用还是很大的,这一点你以后会慢慢明白的。小A刚来,代码不规范,逻辑思维还不完善,所以你作为老员工,就需要用接口来规范代码。”
"哦。。。"我似懂非懂地点了点头。
接着,老板又仔细给我讲解了接口的用法,我这才茅塞顿开。原来,用接口能做这么优雅的事情,看来我在学校学的,还有自己理解的,都还只是皮毛啊,与实际工作脱节了。
先不说接口到底啥概念了,先来看看怎么用接口。现在的菜单是这样子的:
控制台输入1,则登录客户信息。这个登录信息的代码,目前全部写在Application.java里面了,这样会带来一个问题,就是程序不好维护。随着业务逻辑越来越复杂,Application.java 就会越来越难懂。所以,要把这部分业务拆分出去。
老板的意思是,我来制定接口,然后让新来的小A来实现,这样的话,项目的大概思路就不至于滑坡。
创建一个service包,然后创建ICustomerService接口,看下面的步骤图。
注意,下面的Kind是Interface。
接口的定义和类非常像,区别是:
1.接口用的关键字是interface,而不是class
2.接口里面的方法只有声明,没有方法体,这种方法也叫做抽象方法(用abstract关键字修饰)
3.接口里面的方法默认都是public
因为暂时没有接口的实现类(方法都是空的),所以这边只是引用,不去new。
先把原来的保存代码注释掉,然后依次调用接口的方法,顺便说一句,<preconsolas’;font-size:10.5pt;" style=“box-sizing: border-box; font-size: 15px; font-family: Helvetica;”>cstList做成static的,方便调用。</preconsolas’;font-size:10.5pt;">
main方法源码:
代码瞬间清晰了,这就是接口的魅力啊!我只需要负责设计出方法的名字,参数和返回值就可以了,剩下的,我去交给别人做。当然,现在这个程序是不能运行的,因为customerService没有赋值,还是null。
“小A啊,这个接口你帮忙实现一下了哇!”,虽然有点不好意思,但我毕竟是老员工。而且很多业务逻辑已经在main方法里面写了,小A可以去照搬我之前的代码。不过,我也怕小A在别的地方胡乱创建class,所以把实现类也建好了。
接口的实现类必须重写所有抽象方法,CustomerServiceImpl既然实现了ICustomerService,就必须重写所有的抽象方法,这是一种约定。既然你打算遵循我设计的接口,就必须实现我要求的所有方法。
小A基本照搬了我的代码。
还是照搬。
CustomerServiceImpl 是 ICustomerService接口的实现类,ICustomerService是父,CustomerServiceImpl 是子,他们有一种父子关系。父类的引用指向子类对象,这个就是多态。
核心代码
运行:
听说老板曾经在真正的软件公司工作过,这天我跟他请教企业中怎么体现多态了,我看了下我的代码,改了下,然后让我拿去思考。
我看了一下,改动还是挺大的。
1.主类放在了一个包中,所有的类都不推荐放在默认包。
2.创建了一个私有方法 saveCustomer,把service对象当做参数传进去。
方法如下:
目前我对于多态的看法,除了父类引用可以指向子类对象以外,就是这个传参的情况。
其实本质还是一样的,因为app.customerService的真实对象,还是CustomerServiceImpl,是子类对象。
我想了很久,终于明白了,多态的好处就是:代码未动,设计先行!
比如这个例子,我只是设计了这个接口需要哪些方法。
我是接口管理员,是设计人员,我觉得这样设计是合理的,可以完成当前的项目需求。那就ok了,写程序就是这样,得先有图纸,然后图纸要评审,评审完成后再交给施工人员去施工。
java基础班接口
在这个项目里面,我就是那个设计者,新同事小A是技术落地。
那什么时候需要用到多态呢?我想,应该就是有多人协作,项目比较大的时候,必须要先做好设计!然后,开发人员再根据项目经理写好的接口去做技术实现。
“说的不错。”这时候,老板走了过来。
“小白啊,你能悟到这一点,很是难得。没错,多态最核心的意义就是如此,接口的意义就是为了规范开发的代码。”
“那真正的企业里面做项目也有接口管理员吗,是不是也要像这样先做设计?”我激动地问道。
只见老板的脸轻微地抽搐了一下,仿佛想起来各种被烂代码击垮的往事,“这个,小白啊,虽然这样做是最好的。但是很多企业里面,都是以效率优先,也不管到底合不合理,也没有需求和代码评审。都是一个项目分给你,干就完了。”
“…”
答案很显而易见,就是我们需要知道,当我们从这个对象中随便拿出一个元素的时候,还有没有下一个元素?如果有,下一个元素是什么?
如果我们能把这两个问题给解决了,那就可以去遍历了。
所以,我们需要在class中添加两个方法,分别为:
这时候又要看到接口的魅力了,瞧瞧,我们自己都能想到的问题,你说Java的创建者能想不到吗?所以,在 java.util 包里面,就存在一个Iterator接口。这个接口的意思就是“可遍历的”。
如果我们实现了这个接口,就必须要实现里面的hasNext方法和next方法。如果翻开Iterator的源码,我们还会发现有几个方法竟然有方法体。我们重点看这个方法:
这是JDK1.8以后才有的,接口允许写方法体了。我们如果实现了这个接口,也就默认拥有了这个方法。
这个方法干啥用的,待会再说,我们先去实现hasNext方法和next方法。
首先,添加一个新的属性:
遍历的时候,需要专门弄一个属性,避免和currentNode混淆。
然后,编写hasNext方法
思路:第一次调用hasNext方法的时候,nodeForEach肯定是空,如果add成功了,则firstNode肯定不为空,那么就应该返回true。否则,就取nodeForEach.next。那我们在什么时候给nodeForEach赋值呢,自然是next方法。
同样做了是否是第一次的判断,如果是第一次进来,那么给nodeForEach赋值为firstNode,然后直接返回第一个元素的data。如果不是第一次进来,那么就取当前循环节点nodeForEach的next,然后别忘了,一定要重新给nodeForEach赋值为next,指针就偏移过去了。最后,再返回nodeForEach的data。
我怕有些人忘记CustNode咋写的了,所以再把代码贴一下。
解读一下这段源码,<? super E>是泛型,现在你简单理解为是某个类型E的父类,比如E是Customer,那就是<? super Customer>。有趣的是,如果?就是Customer,也是符合规则的。所以,这是一种 >= 的关系。
Consumer<? super E> action,Consumer也是一个接口,意思是消费者。它的源码如下:
看不懂没关系,现在我们只需要知道它里面有一个很重要的方法是accept:
T代表某一个对象,为啥我们要关注这个方法,因为forEachRemaining里面用到了。
Objects.requireNonNull(action);表示判断这个Consumer是否是空,如果为空的话肯定就报错了。
然后是一个while循环,当hasNext方法返回true的时候,就执行action.accept(next());
next() 不就是我们链表的下一个元素嘛,所以整个方法的意思就是,你需要一个Consumer的实现类,比如叫做ConsumerImpl,然后实现accept方法。接着创建ConsumerImpl类的对象,作为参数传入forEachRemaining方法,就可以实现遍历了。
在这个流程中,思考一下,我们最需要关注的是啥,是不是这个accept方法,这个方法就是让我们去设计遍历的时候,对每一个元素做些什么?
可惜了,Java不支持直接传入一个方法, 在Java的语法里面,是不能直接传入一个方法的,在下面的代码中
我们希望直接传入一个Consumer的实现类对象,里面包含了accept方法,至于这个实现类叫什么名字,我们不关心。也许叫ConsumerImpl1,ConsumerImpl2。随便叫什么,我的关注的点其实是在accept方法。
还好,Java提供了一种方案来帮我们偷这个懒,叫做“new一个匿名实现类”,也有的地方叫做匿名内部类。
从表面来看,就是new了一个接口。虽然我们说接口是不能new的,必须要写一个类去实现这个接口,然后去new实现类。但是,从语义上看这个写法,直接理解成new一个接口是非常贴切的。
在CustomerServiceImpl直接写一个main方法来测试。
效果:
hasNext方法有一个bug,就是如果我没有给customers对象调用add方法,直接放进saveToExcel方法,会报错。
你能修改代码,解决这个问题吗?
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/20531.html