一、引例
我们知道,Arrays.sort()方法可以对一个数组进行排序。如果是一个自定义的类,想要使用sort方法,编译器该如何比较自定义类的大小并将数组进行排序呢?
sort方法中存在一条这样的语句:
if(a[i].compareTo(a[j]) > 0)
{
...
}
Java是一种强类型语言,在调用一个方法时,编译器必须要先确认该方法确实存在。
对应此例,在使用sort方法时,必须确保compareTo方法存在。
因此,Java的设计者们想出这样一个办法:设计一个Comparable接口,里面定义了一个compareTo方法。如果你想要使用sort方法去排序自定义的类,该类必须实现(implement) Comparable方法并覆盖其中的compareTo方法。
public interface Comparable<T>//接口,放在package interfaces中
{
int compareTo(T other);
}
public class Employee implements Comparable<Employee>//自定义Employee类实现接口
{
...
public int compareTo(Employee other)//实现接口中定义的方法
{
return Double.compare(salary, other.salary);
}
...
}
var staff = new Employee[3];
...//省略数组的初始化
Arrays.sort(staff);//合法
二、接口的特性
接口不是类,但可以将其与没有实例字段的抽象类做出类比。
1、接口中的所有方法自动声明为public,因此接口中的方法不用在使用public关键字。但在类中实现接口中定义的方法时,需要声明为public。(允许将方法定义为private或public static,但几乎不使用)
2、接口中绝对不可能出现实例字段,但允许出现常量。常量被自动声明为public static final。
public interface Moveable
{
double move();//自动声明为public
double SPEED_LIMIT = 95;//自动声明为public static final
}
3、接口中允许实现简单的方法(标题三中会详细介绍)。
4、接口不是类,因此不允许使用new运算符实现一个接口。但是,允许声明接口的一个变量,该变量必须引用实现了这个接口的类的对象。
// 接上例
Comparable x;
x = new Employee(...);//接口变量引用实现接口的类的对象
5、接口与接口之间,允许存在扩展关系(extends)
// 接上例,Powered接口继承Movable
public interface Powered extends Moveable
{
double milesPerGallon();//自动声明为public
//Powered接口现在有两个方法及一个常量
}
6、一个类只允许继承一个超类,但可以实现多个接口。这也是接口区别于类的最大特性。(一个类既继承超类又实现接口时,extends应位于implements之前)
class Employee extends Person implements Moveable, Comparable
{
...
}
三、接口中的默认方法
1、默认(default)方法的作用:“接口演化”。
假定很早之前你实现了一个Bag类,
public class Bag implements Collection
但随着Java更新,Collection接口在Java 8中增加了一个stream方法。如果不做任何处理,由于原先你写的Bag类中没有实现stream方法,也没有声明为static,就会编译出错。
因此,Java设计者们为了保证“源代码兼容”,将新增的stream方法声明为default,并提供一个默认实现。这样,Bag类就可以正常加载了,并且,当在Bag的一个实例上调用stream方法时,会正常调用Collection.stream方法。
2、默认方法举例:Collection接口
public interface Collection
{
int size();
default boolean isEmpty() { return size() == 0;}
//默认方法允许调用其他方法
}
四、解决继承超类与实现接口中的方法冲突
情况一:接口与接口间的冲突
假定有如下两个接口,它们具有同名的方法,并且至少有一个对该方法进行了默认实现。
此时一个Student类同时实现这两个接口。
public interface Person
{
default String getName(){return "";}
}
public interface Named
{
default String getName(){return getclass().getName();}
}
class Student implements Person, Named{...}//产生冲突,编译器不知道该使用哪一个getName方法。
//正确做法:类中需要覆盖getName方法
class Student implements Person, Named
{
public String getName(){return Person.super.getName();}
}//这样编译器会执行Person中的getName方法
如果接口中同名方法均未有默认实现,则不存在冲突。
情况二:接口与超类间的冲突
将上例中的interface Person改写为 class Person,则不会产生冲突,编译器默认超类方法优先于接口中的默认方法。
public class Person
{
default String getName(){return "";}
}
public interface Named
{
default String getName(){return getclass().getName();}
}
class Student extends Person implements Named{...}//不产生冲突,调用Person中的getName方法