我们知道函数接口可以有default方法和静态方法。我们知道静态方法不属于任何一个类,这里不会有什么问题。但是接口里面有default方法,加上接口可以多继承,这样问题就来了。不信,我们走着瞧。
跳一下。还记得吗:函数接口只允许有一个抽象方法,Lambda表达式默认实现这个抽象方法。比如要对一个参数判断是否满足某条件,一般使用Predicate,默认实现test方法,而且只在程序调用test方法后,整个表达式才执行;比如要对两个参数进行某种操作,而且返回值须与参数同类型,我们可以使用BinaryOperator,默认实现apply方法,只在程序调用apply方法后,整个表达式才执行。当然也有不需传参数的函数接口Supplier,顾名思义,他就像一个供应商,你生产出来的东西,放给Supplier,需要的时候再取出来。是不是说错了,应该是需要的时候才去临时生成东西并返回吧。Supplier只定义了一个get()空方法,非常简洁。我们看看源码。
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
上面有个@FunctionalInterface注释,这个是用来干嘛的呢。它是用来告诉编译器这个接口是函数接口,若不符合函数接口规范(已经说过了),那么就会报编译错误。那么我们自己在写函数接口的时候,注意加上这么一个注释,让编译器帮忙检查一下。
一个函数接口可以有多个default的方法,若有两个函数接口,有相同的default方法时,会怎么样呢。如下面的代码:
public class SingleTest {
FromA fromA = new FromA();
FromB fromB = new FromB();
SubFromB subFromB = new SubFromB();
FromSubAAndA fromSubAAndA = new FromSubAAndA();
XFromBAndA xFromBAndA = new XFromBAndA();
XFromBAndB xFromBAndB = new XFromBAndB();
@Test
public void test(){
fromA.message("fromA"); //接口A的消息:fromA
fromB.message("fromB"); //类FromB的消息:fromB
fromSubAAndA.message("fromSubAAndA");//接口SubA的消息:fromSubAAndA
subFromB.message("subFromB"); //类SubFromB的消息:subFromB
xFromBAndA.message("xFromBAndA"); //类FromB的消息:xFromBAndA
xFromBAndB.message("xFromBAndB"); //类FromB的消息:xFromBAndB
}
}
interface A{
default void message(String msg){
System.out.println("接口A的消息:"+msg);
}
}
interface SubA extends A{
default void message(String msg){
System.out.println("接口SubA的消息:"+msg);
}
}
interface B{
default void message(String msg){
System.out.println("接口B的消息:"+msg);
}
}
class FromA implements A{
}
class FromB implements B{
@Override
public void message(String msg) {
System.out.println("类FromB的消息:"+msg);
}
}
class FromSubAAndA implements SubA,A{
}
class SubFromB extends FromB{
@Override
public void message(String msg) {
System.out.println("类SubFromB的消息:"+msg);
}
}
class XFromBAndA extends FromB implements A{
}
class XFromBAndB extends FromB implements B{
}
代码中的各种关系如图:
运行上面的代码,我们发现当default方法冲突时,Java8里面遵循如下规则:
1、子接口和子类优先
2、类比接口优先
3、如果在接口多重实现中遇到了冲突,必须指定调用哪个接口的default方法
啊?3是什么意思呢?是这个意思,比如我们上面有这样一个类
class FromSubAAndA implements SubA,A{
}
编译是完全可以通过的,运行也是OK的。但是要是有这样一个类呢?同时实现了接口A、B,接口A、B又有同样的default方法。
class FromBAndA implements B,A{
}
这样编译会通不过的。发生这样的事呢,大家都不想的。我们必须重写那个相同的方法,要么另起炉灶,要么指定调用哪个接口的方法。如下:
class FromBAndA implements B,A{
@Override
public void message(String msg) {
//TODO
A.super.message(msg);// 指定使用A接口的default方法
}
}
不要感觉到蛋疼,亲。C++里面是不是也有这样的问题呀?其实当初JAVA没有使用C++的类多重继承,就是为了避免出现这样的情况,现在,还是出现了。