Java核心技術第6章(1)
阿新 • • 發佈:2019-02-15
第6章 介面與內部類
首先介紹介面(interface)技術,這種技術主要用來描述類具有什麼功能,而並不給出每個功能的具體實現.一個類可以實現一個或多個介面,並在需要介面的地方,隨時使用實現了相應介面的物件.瞭解介面以後,再繼續看一下克隆物件.物件的克隆是指建立一個新物件,且新物件的狀態與原始物件的狀態相同.當對克隆的新物件進行修改時,不會影響原始物件的狀態.接下來,看一下內部類(inner class)機制,內部類定義在另外一個類的內部,其中的方法可以訪問包含它們的外部類的域,這是一項比較複雜的技術.內部類技術主要用於設計具有相互協作關係的類集合.
最後介紹代理(proxy),這是一種實現任意介面的物件
6.1 介面
在Java程式設計語言中,介面不是類,而是對類的一組需求描述,這些類要遵從介面描述的統一格式進行定義.如果類遵從某個特定介面,那麼就履行這項服務.下面給出一個具體的示例,Array類中的sort方法承諾可以對物件陣列進行排序,但要求滿足下列前提:物件所屬的類必須實現了Comparable介面.
下面是Comparable介面的程式碼:
public interface Comparable
{
int compareTo(Object other);
}
這樣就是說,任何實現Comparable介面的類都需要包含compareTo方法,並且這個方法的引數必須是一個Object物件,返回一個整型數值.註釋
public interface Comparable<T>
{
int compareTo(T other); // paremeter has type T
}
例如,在實現Comparable<Employee>介面的類中,必須提供下面方法int compareTo(Employee other);
介面中的所有方法自動地屬於 public,因此,在介面宣告方法時,不必提供關鍵字 public .介面還有一個沒有明確說明的附加要求:在呼叫x.compareTo(y)的時候,這個compareTo方法必須確實比較兩個物件的內容,並返回比較的結果.
介面絕不能含有例項域,也不能在介面中實現方法.
假設希望使用Arrays類的sort方法對Employee物件陣列進行排序,Employee類就必須實現Comparable介面.
為了讓類實現一個介面,通常需要下面兩個步驟:
1.將類宣告為實現給定的介面
2.對介面中的所有方法進行定義
要將類宣告為實現某個介面,需要使用關鍵字 implements:
class Employee implements Comparable
這裡的Employee需要提供compareTo方法.public int compareTo(Object otherObject)
{
Employee other = (Employee)otherObject;
return Double.compare(salary, other.salary);
}
這裡使用了靜態Double.compare方法.警告:在介面宣告中,沒有將compareTo方法宣告為 public,這是因為在介面中的所有方法都自動地是 public,不過在實現介面時,必須把方法宣告為 public .
現在已經看到,要讓一個類使用排序服務必須讓它實現compareTo方法.這是理所當然的,因為要向sort方法提供物件的比較方式.但是為什麼不能在Employee類直接提供一個compareTo方法,而必須實現Comparable介面呢?
主要原因在於Java程式設計語言是一種強型別(strongly typed)語言(有點疑惑).在呼叫方法的時候,編譯器將會檢查這個方法是否存在.在sort方法中可能存在下面這樣的語句:
if (a[i].compareTo(a[j]) > 0)
{
// rearrange a[i] and a]j\
...
}
為此,編譯器必須確認a[i]一定有compareTo方法,如果a是一個Comparable物件的陣列,就可以確保擁有compareTo方法,因為這個實現Comparable介面的類都必須提供這個方法的定義.程式6-1給出了對一個Employee類例項陣列進行排序的完成程式碼.
interfaces/EmployeeSortTest.java如下所示:
package interfaces;
import java.util.Arrays;
/**
* This program denonstrates the use of the Comparable interface
*/
public class EmployeeSortTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl", 75000);
staff[1] = new Employee("Hacker", 35000);
staff[2] = new Employee("Tommy", 50000);
System.out.println("Before sort:");
for (Employee e : staff)
System.out.println("name = " + e.getName() + ", salary = " + e.getSalary());
Arrays.sort(staff);
System.out.println("After sort:");
for (Employee e : staff)
System.out.println("name = " + e.getName() + ", salary = " + e.getSalary());
}
}
interfaces/Employee.java如下所示:package interfaces;
public class Employee implements Comparable<Employee>
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
/**
* Compares employees by salary
* @param other another Employee object
* @return a negative value if this employee has a lower salary than
* otherObject, 0 if the salaries are the same, a positive value otherwise
*/
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
}
執行結果如下所示:java.lang.Comparable<T>方法如下所示:
int compareTo(T other);
用這個物件與other進行比較.小於other返回負值;等於other返回0;大於other返回正值.java.util.Arrays方法如下所示:
static void sort(Object[] a);
使用mergesort演算法對陣列a中的元素進行排序,要求陣列中的元素必須實現了Comparable介面的類,並且元素之間是可比較的.6.1.1 介面的特性
介面不是類,尤其不能使用 new 運算子例項化一個介面:x = new Comparable(...); // error
然而,儘管不能構造介面的物件,卻能宣告介面的變數:Comparable x; // ok
介面變數必須引用實現了介面的類物件:x = new Employee(...); // ok provided Employee implements Comparable
接下來,如同使用 instanceof 檢查一個物件是否屬於某個特定類一樣,也可以使用 instance 檢查一個物件是否實現了某個特定的介面:if (anObject instanceof Comparable) { ... }
與可以建立類的繼承關係一樣,介面也可以被擴充套件.這裡允許存在多條從具有較高通用性的介面到較高專用性的介面的鏈.例如,假如有一個稱為Moveable的介面:public interface Moveable
{
void move(double x, double y);
}
然後,可以以它為基礎擴充套件一個叫做Powered的介面:public interface Powered extends Moveable
{
double milesPerGallon();
}
雖然在介面中不能包含例項或靜態方法,但卻可以包含常量.例如:public interface Powered extends Moveable
{
double milePerGallon();
double SPEED_LIMIT = 95; // a public static final constant
}
與介面中的方法都自動地被設定為 public 一樣,介面中的域將被自動地設為 public static final .儘管每個類只能夠擁有一個超類,但可以實現多個介面.例如,Java程式設計語言有一個非常重要的內建介面,稱為Cloneable,如果某個類實現了這個Cloneable介面,Object類中的clone方法就可以建立類物件的一個拷貝.如果希望自己設計的類擁有克隆和比較的能力,只要實現這兩個介面就可以了.
class Employee implements Cloneable, Comparable
使用逗號將實現的各個介面分隔開.6.1.2 介面與抽象類
為什麼Java程式設計語言還要不辭辛苦地引入介面概念?為什麼不將Comparable直接設計成如下所示的抽象類.abstract class Comparable
{
public abstract int compareTo(Object other);
}
然後,Employee再直接擴充套件這個抽象類,並提供compareTo方法的實現:class Employee extends Comparable
{
public int compareTo(Object other) { ... }
}
非常遺憾,使用抽象類表示通用屬性存在這樣一個問題:每個類只能擴充套件於一個類.假設Employee類已經擴充套件於一個類,例如Person,它就不能再像下面這樣擴充套件第二個類了:class Employee extends Person, Comparable // error
但每個類都可以像下面這樣實現多個介面:class Employee extends Person implements Comparable // ok
有些程式設計語言允許一個類有多個超類,例如C++.稱此特性為多繼承.而Java的設計者選擇了不支援多繼承,其主要原因是多繼承會讓語言本身變得非常複雜.實際上,介面可以提供多重繼承的大多數好處,同時還能避免多重繼承的複雜性和低效性.
註釋:C++具有多繼承,隨之帶來了一些諸如虛基類,控制規則和橫向指標型別轉換等複雜特性.很少有C++程式設計師使用多繼承,有些程式設計師建議只對"混合"風格的繼承使用多繼承.在"混合"風格中,一個主要的基類描述父物件,其他的基類(因此稱為混合)扮演輔助的角色.這種風格類似於Java類中從一個基類派生,然後實現若干個輔助介面.然而,在C++中,"混合"類可以新增預設的行為,而Java的介面則不行.
關於介面與抽象類有一篇非常好的文章,.關鍵部分截圖如下: