介面
介面概念
介面是對類的一組需求的描述,類遵從特定的描述,實現這項服務。
例如:
這是Camprable介面
public interface Comparable<T>
{
int compareTo(T other);
}
複製程式碼
然後在類中實現這一介面:
- 將類宣告為實現給定的介面
- 對介面中所有方法進行定義
class Employee implements Comparable<Employee>
{
...
public int compareTo(Employee other) {
return Double.compare(salary, other.salary);
}
...
}
複製程式碼
介面特性
- 介面不是類,不能用new例項化一個介面,可是能宣告介面變數,然後再引用實現了該介面的類物件
Compararble x;
x = new Employee(...); //ok
複製程式碼
解釋:
可能會問這樣的介面變數有啥用呢??舉個定時器的例子,Timer函式 需要接收 一個操作函式 ,但是我們不能直接傳函式進去,所以我們把介面變數(引用實現了該介面的類)傳進去。同時我們可以發現介面裡的方法不是靜態方法,因為我們需要先新建物件。
當然 javase8 中可以用 lambda表示式 傳函式,見下文 lambda 表示式
- 檢查某物件是否實現某介面
if(anObject instanceof Comparable) {...}
- 介面也可以擴充套件
public interface Powered extends Moveable
- 介面中不含例項域,卻可以包含常量(publice static final)
- 可以為介面提供預設實現
public interface Comparable<<T>
{
default int compareTo(T other){return 0};
}
複製程式碼
-
一個類可以有多個介面
-
繼承時容易出現的問題:
例如子類繼承compareTo方法時,必須要有子類與超類進行比較的思想,就像第五章equals方法一樣,不然會有ClassCastException錯誤。
- 只有一個抽象方法,並用lambda表示式替代,這種介面叫函式式介面;不含任何方法的介面叫標記介面,如 Cloneable。
解決介面衝突
同名,且引數型別相同下:
1)超類優先
2)介面衝突:只要其中一個介面提供了預設方法,就會報錯;如果都不提供,就是抽象類。
ps. 類優先的機制確保了與java SE 7 的相容,因為那時沒有預設方法。
lambda表示式
為什麼引入lambda表示式呢?
Arrays.sort(strings, new LengthComparator());
這段程式碼用定製的比較器完成字串排序,可以看出它傳入了一個物件(介面),這個物件實現了Camparable介面,物件裡面有compare方法。我們主要目的就是想傳遞這個方法,但是Java之前只能通過構造物件,來傳遞該方法。於是lambda表示式正式引入!
lambda表示式語法
例子:
(String first, String second) -> {...}
把它替換到之前到程式碼中:
Array.sort(strings, (fisrt, second)->first.length() - second.length());
一些細節:
- 如果可以推匯出引數型別,那麼型別可以不寫;而且如果只有這一個引數,小括號也可以不寫。
- 如果某些分支返回一個值,而另一些分支不返回值,這是不合法的。
(int) -> {if(x >= 0) return 1;} //不合法
- 對於只有一個抽象方法的介面,可以提供一個lambda,就像例子;這種介面稱為函式式介面。常的用函式式介面有:Predicate, Consumer, Function, Supplier 等等。
方法引用與lambda
假如想傳遞的 lambda 已經有了現成的方法,那麼可以用方法引用來代替lambda。 三種情況:
-
靜態方法
- Lambda:
(args) -> ClassName.staticMethod(args)
- 方法引用:
ClassName::staticMethod
- Lambda:
-
現有物件的例項方法
- Lambda:
(args) -> expr.instanceMethod(args)
- 方法引用:
expr::intanceMethod
- Lambda:
-
某類的例項方法
- Lambda:
(arg0, rest) -> arg0.instanceMethod(rest)
- 方法引用:
ClassName::instanceMethod //arg0 是 ClassName 型別的
- Lambda:
變數作用域
lambda 表示式可以訪問外圍方法或類中的變數,但需注意:
- 該變數必須是最終變數,不可以重新賦值;如String
- 在 lambda 表示式中宣告與一個區域性變數同名的引數或區域性變數是不合法的。
- 在一個 lambda 表示式中使用 this 關鍵字時, 是指建立這個 lambda 表示式的方法的 this 引數。 簡單說就是沒變化。