接口是一个抽象概念的实例化,它表示了一个类所能实现的方法与属性值。但在Java中,interface
作为一个类型,就额外拥有了一些特点。
以下内容基于Java8书写
定义一个接口
接口被interface
关键字定义且无法被final
所描述,自带有abstract
修饰符。在不同的位置下,接口修饰符的限制有所不同:
- 同名类文件中(独立接口),只允许
public
权限修饰符,不接受static
关键字。 class
内部类,接受public
、protect
、private
权限修饰符,默认且只允许static
修饰符。interface
内部类,自带且只允许public static abstract
修饰符。
举例:
1 | public interface AInterface { |
定义接口方法
接口方法只接受public abstract
修饰符,而default
修饰符在接口方法中只表示默认实现,所处位置在权限修饰符后与abstract
同级,因此不提供权限修饰作用。被default
修饰的接口方法不再强制实现类实现。↓
1 | public interface AInterface { |
这里的BClass
在实现AInterface
时就不需要实现hello()
方法。
定义接口属性
接口属性只能被public static final
修饰(所以一般可以不写修饰符),因此存在于接口中的属性必定可以使用接口名.属性名
方式进行访问。
例如java.lang.reflect.Member
接口就定义了PUBLIC
和DECLARED
属性。↓
注意
Java8中不支持本地接口(方法中定义接口)
Java7及以前版本的接口方法无法在接口中实现(对接口方法进行default实现)
接口的使用
接口可以被实现与继承,并且接口是Java中唯一支持多继承的类型。
接口的继承
例如以下接口定义中,AInterface
继承于Map
、Cloneable
和Serializable
接口,这里需要说明一点,interface只能继承interface,class只能实现interface,interface无法继承class。↓
1 | public interface AInterface<K, V> extends Map<K, V>, Cloneable, Serializable { |
这表示,AInterface
接口也继承了Map
、Cloneable
和Serializable
接口的属性与方法,也就是说可以像使用Map
、Cloneable
和Serializable
接口一样去使用AInterface
接口。
接口的实现
比如经典的HashMap
类就是实现了Map
、Cloneable
和Serializable
接口。↓
因此,HashMap
就需要实现它继承的接口的所有非default修饰方法。当然,实现类也可以重写被default
修饰的方法。
作用
定义规范
接口的主要作用就是定义一个规范,就像是协议一样,让协议实现方通过接口实现的方式进行统一开发,而使用方只需要关注协议内容即可。双方都面向接口开发可以大大降低耦合度,极大地提高开发维护效率。
举例:
假设我们有一个县长接口:↓
1 | public interface Xianzhang { |
县长接口为我们提供了power
和wealth
两个方法。这时我们用一个类Tang
来实现县长接口:↓
1 | public class Tang implements Xianzhang { |
此时,Tang
就获得了县长类型,我们就可以这样来使用它:↓
1 | public static void main(String[] args) throws Exception { |
这里我们就获得了Tang
的power
和wealth
。那么我们就有一个疑问了,为什么要使用Xianzhang tang = new Tang();
为不是Tang tang = new Tang();
呢,明明效果都一样啊?因为我们可能还有一个县长的实现类Wang
:↓
1 | public class Wang implements Xianzhang { |
当我们没有Tang
的时候,只需要把new Tang()
换成new Wang()
就好了,handle()
方法也不需要改变,因为我们定义是以Xianzhang
来定义的,我们就只需要关注Xianzhang
接口为我们提供了什么方法就可以了,谁来实现都无所谓。
定义常量
很多时候我们会用到一些配置量,例如每周的天数、光速、字符串分隔符等等。
我们需要一个专有的类来管理这些全局配置,那么接口就非常适合用来存放这些静态常量:↓
1 | // 这里只做演示,常量在实际编写时需要归类,使用不同的接口管理 |
当然,并不是说静态常量都要放在接口中,只是因为放置于接口中便于维护与使用。
定义类型
定义类型的目的一般用在类筛选上,某些方法只对特殊的类做处理就可以使用空接口,例如Serializable
接口:↓
当我们实现去定义类型的空接口时,就表示我们知道我们在做什么,类似于签署了一个协议。
比如我开发了一个功能,传入一个对象就可以将这个对象的所有属性值清空。但是为了避免使用者误用这个方法,我可以定义一个Clearable
接口,并将clear
方法入参锁定成Clearable
。这样,只有继承了Clearable
接口的类的实例才允许被属性清空:
1 | // 属性清空方法 |