一、接口中的静态方法
在java中接口设计的初衷是作为抽象的规范,接口不是类,而是对类的一组需求描述。在java SE8之前,静态方法通常都是放在工具类中,如Collecation接口对应的工具类是Collecations。但在java SE8的新特性中允许在接口中添加静态方法。
在接口中定义静态方法和在普通类中定义没有区别,如下所示:
public interface InterfaceStaticMethodTest {
public static void staticMethod(){
System.out.println("interface static method test!");
}
}
注:接口中的所有方法都自动为public,上面代码中的public关键可有可无。
接口中的静态方法调用:
接口名称.方法名() 如:IInterfaceStaticMethodTest.staticMethod()
public class SubClass {
public static void main(String[] args) {
InterfaceStaticMethodTest.staticMethod();
System.out.println("sub class");
}
}
二、接口中的默认方法
同样,在java SE8中,允许在接口中定义包含方法体的默认方法,在这之前是绝对不允许的。默认方法的一个重要用法是“接口演化”(interface evolution),假设有这么一个情景。很久以前我们定义了一个接口,而且很多类实现了这个接口,现在我们想在这个接口中增加一个方法,那在这java SE8之前,我们需要修改所有的实现类来重写这个方法,才能保证程序顺利运行。有了默认方法这个新特性后,就可以通过在接口中添加默认方法的来解决这个问题,不需要去修改每一个实现类。
1、默认方法的定义: default [public] 返回类型 方法名([方法参数...]){ statement }
接口中的默认方法必须有default关键字修饰。public 关键字可有可无,可以在default之前或之后。
public interface InterfaceDefaultMethodTest {
default void testMethod(){
System.out.println("interface default method run!");
}
}
2、解决默认方法冲突
在java中,一个类可以继承一个父类,同时可以实现多个接口,假设我们继承的父类中定义了一个名称及参数都与接口中的默认方法一致,那java虚拟机将如何处理。先来看最简单情况,继承一个父类及实现一个接口。
/**
* 包含sayName方法的父类
*/
public class Father {
void sayName(){
System.out.println("Father class sys name!");
}
}
/**
* 包含sayName的接口
*/
public interface FatherInterface {
default void sysName(){
System.out.println("father interface sys name!");
}
}
/**
* 继承Father及实现FatherInterface的子类
*/
public class Son extends Father implements FatherInterface{ }
上面定义了一个Father类、一个FatherInterface接口,他们都定义了同样的方法sayName,Son是继承Father及实现FatherInterface的子类。
先看一下Son调用sayName()方法会是什么效果。
从运行结果看,Son调用的Father类中的方法,其实这里使用的是java中的超类优先规则。
超类优先:如果超类提供了一个具体的方法,同名而且有相同参数类型的默认方法会被忽略。
现在我们再来看另一种情况,就是一个类同时实现了多个接口,这些接口中都同时定义了同名而且有相同参数的默认方法。
/**
* 包含sayName的Father接口
*/
public interface FatherInterface {
default void sysName(){
System.out.println("FatherInterface interface sys name!");
}
}
/**
* 包含sayName的Mother接口
*/
public interface MotheraInterface {
default void sysName(){
System.out.println("MotherInterface interface sys name!");
}
}
/**
* 编译错误,
* 同时实现Father接口及Mother接口
*/
public class Son implements FatherInterface,MotherInterface{ }
编译器提示错误信息:Son inherits unrelated defaults for sysName() from types FatherInterface and MotherInterface。
以上代码定义了一个Father接口及Mother接口,都包含了相同的默认方法,Son类同时实现了他们,但是没有重写sayName方法,此时编辑会给出无法明确从哪个接口中继承sayName方法的错误提示,编译无法通过。处理以上错误就得在Son类中重写sayName方法。
public class Son implements FatherInterface,MotherInterface{
@Override
public void sysName() {
System.out.println("Son say name!");
}
}
public class TestInterfaceDefaultMethod {
public static void main(String[] args) {
Son son = new Son();
System.out.println("********************");
son.sysName();
System.out.println("********************");
}
}
上面的重写是完全不用接口中的定义,而是全新的实现,假设明确需要调用MotheraInterface中的默认方法,那可以在实现类中直接调用,如下所示:
重新修改Son类中的sayName方法:
public class Son implements FatherInterface,MotherInterface{
@Override
public void sysName() {
//掉用MotherInterface中的sysName方法
MotherInterface.super.sysName();
}
}
调用了MotherInterface中的sayName方法。
需要注意的是,即使上面的两个接口其中有一个没有具体的实现,如下定义:
/**
* 没有实现包含sayName方法的Father接口
*/
public interface FatherInterface {
void sysName();
}
程序还是会编译错误,java设计者更强调的是一致性,程序员必须解决这样的二义性。
接口冲突:如果一个接口提供了一个默认方法,另一个接口也提供了一个同名而且参数类型相同的方法,必须覆盖这个方法来解决冲突。
最后一种情况,就是Son继承了Father类,及实现了FatherInterface接口,但是FatherInterface中的sayName没有实现,那在Son类中依然需要重写sayName()方法,此处就不再贴代码。