当前位置: 首页 > 科技观察

Java8有多棒?打破你对接口的所有认知!

时间:2023-03-12 00:08:55 科技观察

前段时间面试了一个39岁的程序员,结果不是很理想。如果您还没有阅读,请单击此处阅读。最近也面试了一些Java程序员,很多都有4、5年的工作经验。当我向他询问Java8的一些新特性时,他们大多答不上来。比如下面这个问题:Stacklength:方法可以写在接口里吗?小A:当然默认是抽象方法。栈长:实现方法可以写在接口里吗?小A:不行,所有的方法都必须是抽象的。堆栈管理器:你确定吗?小A:好吧……小A好像有点怀疑我的问题,他心里肯定在猜我不会给他安葬。后来他仔细想了想,最后坚定地告诉我:接口里只能写抽象方法,不能写实现方法。栈长:在接口中写实现方法是可以的,从Java8开始,你用过Java8吗?小A:嗯,看来我学习不好,对Java8略有了解,比如Lambda表达式,但是在实际项目中用处不大。通过与小A的交流,我也看到了很多开发者的问题。虽然开发版使用的是Java8,但实际使用还是Java8之前最基本的语法,对Java8的新特性我也没有什么了解。从2014年Java8发布到现在已经6年了,最新的Java14已经发布。OK,这不在本文讨论范围之内。Java8+系列教程请关注公众号Java技术栈回复“java”阅读,本文只是想跟着小A问的问题,什么是默认方法和静态方法?上面说了,Java8可以通过方法来实现,可以在接口中加入默认方法和静态方法。默认方法用default修饰,只能在接口中使用,静态方法用static修饰,我们比较熟悉。并且接口中可以同时存在多个默认方法和静态方法。在接口中编写实现方法并不少见。这样的用法从Java8到Java14遍地开花,接口默认方法和静态方法随处可见。比如我们看一下java.util.Map在JDKAPI中接口默认方法和静态方法上的应用。/**来源公众号:Java技术栈*/publicinterfaceMap{.../***接口默认方法*/defaultbooleanremove(Objectkey,Objectvalue){ObjectcurValue=get(key);if(!Objects.equals(curValue,value)||(curValue==null&&!containsKey(key))){returnfalse;}remove(key);returntrue;}.../***接口静态方法*/publicstatic,V>Comparator>comparingByKey(){返回(Comparator>&Serializable)(c1,c2)->c1.getKey().compareTo(c2.getKey());}...}为什么我们有接口默认方法?举一个很现实的例子:我们的接口早就写好了,后来由于各种业务问题,不可避免地要修改接口。Java8之前,比如要给一个接口添加一个抽象方法,所有的接口实现类都必须实现这个方法,否则会出现编译错误,而有些实现类根本不需要实现这个方法和被迫要写一个空的实现,改动会很大。所以接口默认的方法就是解决这个问题。只要在一个接口上增加一个默认方法,所有的实现类都会自动继承它。无需改动任何实现类,不影响业务。另外,接口默认方法可以被接口实现类重写。为什么会有接口静态方法?接口静态方法类似于默认方法,只是接口静态方法不能被接口实现类覆盖。接口静态方法只能通过接口名直接调用。静态方法所在的静态方法名。接口默认方法多重继承冲突问题由于接口默认方法可以被继承和重写,如果相同的默认方法存在于多个继承的接口中,就会出现冲突问题。下面我将列出3个示例冲突场景。看看下面的程序有没有冲突:/**来源公众号:Java技术栈*/interfacePeople{defaultvoideat(){System.out.println("Peopleeat");}}/**来源公众号:Java技术栈*/interfaceMan{defaultvoideat(){System.out.println("ManEats");}}/**来源公众号:Java技术栈*/interfaceBoyextendsMan,People{}Boy同时继承了People和Man,在这个时候IDEA编辑器会报错:这是接口多重继承导致的冲突问题。男孩不知道继承谁。这显然是个问题。IDEA也会提示需要重写这个方法来解决问题。:/**来源公众号:Java技术栈*/interfaceBoyextendsMan,People{@Overridedefaultvoideat(){System.out.println("Boyeats");}}指定父接口的默认方法也可以直接调用方法中,例如:/**来源公众号:Java技术栈*/interfaceBoyextendsMan,People{@Overridedefaultvoideat(){People.super.eat();Man.super.eat();System.out.println("Boyeat");}}再添加一个实现类进行测试:/**来源公众号:Java技术栈*/staticclassStudentimplementsBoy{publicstaticvoidmain(String[]args){Studentstudent=newStudent();student.eat();}}输出:Maneats,maneats,boyeats嗯,很强大!冲突2我们再换一种写法,从People继承Man,然后Man重写People中的默认方法。这时候编辑器并没有报错,但是People的默认方法是灰色的,没有使用提示。再次运行上面的例子,输出:Maneats因为Man继承自People,所以Man又重新设置了默认方法。显然,这时候Boy就知道继承谁的默认方法了。冲突3在Man接口中添加一个新方法:say,然后在Boy接口中添加一个默认方法:say。这个时候Man中的抽象方法居然被忽略了,IDEA都提示没有使用。这显然是优先于抽象方法的默认方法。总结本文介绍了Java8的默认方法和静态方法,以及默认方法冲突问题的解决方法。所以,出去面试的时候,别说接口不会写实现方法,太OUT了。.文章只举了3种默认方法的冲突场景,不能确定没有更多的冲突问题。默认方法虽然解决了接口变化带来的问题,但是如果设计不当或者设计过度,由此带来的方法冲突问题也是需要注意的。本文转载自微信公众号“Java技术栈”,可通过以下二维码关注。转载本文请联系Java技术栈公众号。