接口指定了類必須執行的操作,但并不指定具體的方法,可以把接口當作類的大綱或者藍圖(如果一個類實作了一個接口,但并沒有為該接口中的所有方法提供方法主體,那麼該類必須是抽象類)。
類實作接口的步驟
- 将類聲明為實作給定的接口
- 對接口中的所有方法進行定義
舉例如下
class Employee implements Comparable //implements關鍵字表示類實作某個接口
{
...
public int compareTo(Object otherObject) //對接口中的所有方法進行定義
{
Employee other = (Employee) otherObject;
return Double.compare(salary,other.salary)
}
}
繼承中可能存在的問題
請看例子
class Manager extends Employee
{
public int compareTo(Employee other)
{
Manager otherManager = (Manager) other;
}
...
}
在繼承的過程中,Manager覆寫了compareTo方法,在進行比較時,假設x是一個Employee對象,y是一個Manager對象,調用x.compareTo(y)時不會出現異常,但是當調用y.compareTo(x)時,會抛出ClassCastException。
解決辦法
如果子類之間的比較含義不一樣,不同子類之間無法比較,那以上例子就屬于非法比較,應該在比較的方法前進行以下檢測
如果子類之間有通用的比較方法,則應該在超類中提供一個比較方法,并将該方法聲明為final。
接口的特性
- 無法構造接口的對象,但能聲明接口的變量
x = new Compareble(...); //ERROR
Comparable x; //OK
- 可以使用instance檢查一個對象是否實作了某個特定的接口
- 如同建立類的繼承關系一樣,接口可以被擴充
public interface Moveable
{
void move(double x, double y);
}
public interface Powered extends Moveable //擴充接口
{
double milesPerCallon();
}
- 之是以引入接口的概念,是因為Java中不存在多重繼承,引入接口可以提供多重繼承的大多數好處,同時還能避免多重繼承的複雜性和低效性。
預設方法
可以為接口方法提供一個預設實作,但必須用default修飾符标記這樣一個方法。如下
public interface Comparable<T>
{
default int comparableTo(T other){return 0;}
}
預設方法的一個重要用法是“接口演化”。舉例,以Collection接口為例,假設初始時提供了這樣一個類
後來,在Java SE 8中又為該接口增加了一個stream方法,如果stream方法不是預設方法,由于Bag類中沒有實作這個新方法,是以Bag類将編譯失敗。
Java中解決預設方法沖突
- 超類優先。如果超類提供了一個具體方法,同名且有相同參數類型的預設方法會被忽略
-
接口沖突。如果一個超接口提供了一個預設方法,另一個接口提供了
一個同名而且參數類型相同的方法,那麼必須覆寫這個方法來解決沖突。舉例如下
interface Named
{
default String getName() {return getClass().getName()+"-"+hashCode();}
}
interface Person
{
default String getName() {return getClass().getName();}
}
//假設有一個類同時實作了這兩個接口
class Student implements Person,Named
{
public String getName() {return Person.super.getName();} //覆寫兩個接口中的getName方法
}