理解Functional Interface(函數式接口,如下簡稱FI)是學習Java8 Lambda表達式的關鍵所在,因此放在最開始討論。FI的定義其實很簡單:任何接口,若是隻包含惟一一個抽象方法,那麼它就是一個FI。爲了讓編譯器幫助咱們確保一個接口知足FI的要求(也就是說有且僅有一個抽象方法),Java8提供了@FunctionalInterface註解。舉個簡單的例子,Runnable接口就是一個FI,下面是它的源代碼:html
@Functional Interface
public interface Runnable{
public abstract void run ();
}
public interface Runnable {
public abstract void run ();
}
複製代碼
ps: 上述兩種都是函數式接口,在Java 8 提供新的註解Function Interface
聲明一個接口爲函數式接口,聲明以後這個接口必須符合函數式接口的規範。@FunctionalInterface 對於接口是否是函數式接口沒有影響,但該註解知識提醒編譯器去檢查該接口是否僅包含一個抽象方法。java
下面的用法就是錯誤:bash
@Function Interface
public interface test{
public void test1();
public void test2();
複製代碼
ps: 接口中聲明的方法默認是抽象的,使用註解後,編譯器自動檢查發現存在兩個抽象方法,會報錯。app
@FunctionalInterface
interface GreetingService {
void sayMessage(String message);
default void doSomeMoreWork1() {
// Method body
}
default void doSomeMoreWork2() {
// Method body
}
}
複製代碼
@FunctionalInterface
interface GreetingService {
public void sayMessage(String message);
public static void printHello(){
System.out.println("Hello");
}
}
複製代碼
@FunctionalInterface
interface GreetingService {
void sayMessage(String message);
@Override
boolean equals(Object obj);
}
複製代碼
爲何講內部類,由於在 lamada 表達式又與 Java 內部類應用存在類似之處,特別是匿名內部類。在學習這部分前專門又複習了一遍內部類的概念。ide
成員內部類
局部內部類
靜態內部類
匿名內部類函數
內部類 對象名 = 外部類對象.new 內部類( );
public class Outer {
private static int i = 1;
private int j = 10;
private int k = 20;
public static void outerF1() {
}
/** * 外部類的靜態方法訪問成員內部類,與在外部類外部訪問成員內部類同樣 */
public static void outerF4() {
//step1 創建外部類對象
Outer out = new Outer();
//step2 根據外部類對象創建內部類對象
Inner inner = out.new Inner();
//step3 訪問內部類的方法
inner.innerF1();
}
public static void main(String[] args) {
/* * outerF4();該語句的輸出結果和下面三條語句的輸出結果同樣 *若是要直接建立內部類的對象,不能想固然地認爲只需加上外圍類Outer的名字, *就能夠按照一般的樣子生成內部類的對象,而是必須使用此外圍類的一個對象來 *建立其內部類的一個對象: *Outer.Inner outin = out.new Inner() *所以,除非你已經有了外圍類的一個對象,不然不可能生成內部類的對象。由於此 *內部類的對象會悄悄地連接到建立它的外圍類的對象。若是你用的是靜態的內部類, *那就不須要對其外圍類對象的引用。 */
Outer out = new Outer();
Outer.Inner outin = out.new Inner();
outin.innerF1();
}
public void outerF2() {
}
/** * 外部類的非靜態方法訪問成員內部類 */
public void outerF3() {
Inner inner = new Inner();
inner.innerF1();
}
/** * 成員內部類中,不能定義靜態成員 * 成員內部類中,能夠訪問外部類的全部成員 */
class Inner {
// static int innerI = 100;內部類中不容許定義靜態變量
// 內部類和外部類的實例變量能夠共存
int j = 100;
int innerI = 1;
void innerF1() {
System.out.println(i);
//在內部類中訪問內部類本身的變量直接用變量名
System.out.println(j);
//在內部類中訪問內部類本身的變量也能夠用this.變量名
System.out.println(this.j);
//在內部類中訪問外部類中與內部類同名的實例變量用外部類名.this.變量名
System.out.println(Outer.this.j);
//若是內部類中沒有與外部類同名的變量,則能夠直接用變量名訪問外部類變量
System.out.println(k);
outerF1();
outerF2();
}
}
}
複製代碼
在方法中定義的內部類稱爲局部內部類。與局部變量相似,局部內部類不能有訪問說明符,由於它不是外圍類的一部分,可是它能夠訪問當前代碼塊內的常量,和此外圍類全部的成員。學習
public class Outer {
private int s = 100;
private int outI = 1;
public static void main(String[] args) {
// 訪問局部內部類必須先有外部類對象
Outer out = new Outer();
out.f(3);
}
public void f(final int k) {
final int s = 200;
int i = 1;
final int j = 10;
/**
* 定義在方法內部
*/
class Inner {
// 能夠定義與外部類同名的變量
int s = 300;
int innerI = 100;
// static int m = 20; 不能夠定義靜態變量
Inner(int k) {
innerF(k);
}
void innerF(int k) {
// java若是內部類沒有與外部類同名的變量,在內部類中能夠直接訪問外部類的實例變量
System.out.println(outI);
// 能夠訪問外部類的局部變量(即方法內的變量),可是變量必須是final的
System.out.println(j);
//System.out.println(i);
// 若是內部類中有與外部類同名的變量,直接用變量名訪問的是內部類的變量
System.out.println(s);
// 用this.變量名訪問的也是內部類變量
System.out.println(this.s);
// 用外部類名.this.內部類變量名訪問的是外部類變量
System.out.println(Outer.this.s);
}
}
new Inner(k);
}
}
複製代碼
若是你不須要內部類對象與其外圍類對象之間有聯繫,那你能夠將內部類聲明爲
static
。這一般稱爲嵌套類(nested class)。想要理解static應用於內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向建立它的外圍類對象。然而,當內部類是static的時,就不是這樣了。 要建立嵌套類的對象,並不須要其外圍類的對象。 不能從嵌套類的對象中訪問非靜態的外圍類對象。ui
public class Outer {
private static int i = 1;
private int j = 10;
public static void outerF1() {
}
public static void main(String[] args) {
new Outer().outerF3();
}
public void outerF2() {
}
public void outerF3() {
// 外部類訪問內部類的靜態成員:內部類.靜態成員
System.out.println(Inner.inner_i);
Inner.innerF1();
// 外部類訪問內部類的非靜態成員:實例化內部類便可
Inner inner = new Inner();
inner.innerF2();
}
/**
* 靜態內部類能夠用public,protected,private修飾
* 靜態內部類中能夠定義靜態或者非靜態的成員
*/
static class Inner {
static int inner_i = 100;
int innerJ = 200;
static void innerF1() {
// 靜態內部類只能訪問外部類的靜態成員(包括靜態變量和靜態方法)
System.out.println("Outer.i" + i);
outerF1();
}
void innerF2() {
// 靜態內部類不能訪問外部類的非靜態成員(包括非靜態變量和非靜態方法)
// System.out.println("Outer.i"+j);
// outerF2();
}
}
}
複製代碼
這是咱們今天的主角,匿名內部類, 字面意思沒有名字的類
{}
。匿名內部做爲最特殊的內部類,須要講解的內容。(think in java)this
爲何使用匿名內部類:編碼
在使用匿名內部類時,要記住如下幾個原則:
你可能見過以下的代碼:
List<Integer> var1 = new ArrayList<Integer>()
{
{
add(1);
add(2);
}
};
複製代碼
就是用到匿名類的語法糖。
// 在方法中返回一個匿名內部類
public class Parcel6 {
public static void main(String[] args) {
Parcel6 p = new Parcel6();
Contents c = p.cont();
}
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() {
return i;
}
}; // 在這裏須要一個分號
}
}
複製代碼
cont()方法將下面兩個動做合併在一塊兒:返回值的生成,與表示這個返回值的類的定義。 return new Contents() 可是,在到達語句結束的分號以前,你卻說:「等一等,我想在這裏插入一個類的定義」:
這種奇怪的語法指的是:「建立一個繼承自Contents的匿名類的對象。」經過new 表達式返回的引用被自動向上轉型爲對Contents的引用。匿名內部類的語法是下面例子的簡略形式:
class MyContents implements Contents {
private int i = 11;
public int value() {
return i;
}
}
return new MyContents();
複製代碼
上述這類寫法是最多見。
在Java中,一般就是編寫另一個類或類庫的人規定一個接口,而後你來實現這個接口,而後把這個接口的一個對象做爲參數傳給別人的程序,別人的程序必要時就會經過那個接口來調用你編寫的函數,執行後續的一些方法。
public class CallBack {
public static void main(String[] args) {
CallBack callBack = new CallBack();
callBack.toDoSomethings(100, new CallBackInterface() {
public void execute() {
System.out.println("個人請求處理成功了");
}
});
}
public void toDoSomethings(int a, CallBackInterface callBackInterface) {
long start = System.currentTimeMillis();
if (a > 100) {
callBackInterface.execute();
} else {
System.out.println("a < 100 不須要執行回調方法");
}
long end = System.currentTimeMillis();
System.out.println("該接口回調時間 : " + (end - start));
}
}
public interface CallBackInterface {
void execute();
}
複製代碼
Java裏的回調,能夠說是匿名內部類精彩表演,優美的編碼風格,真是讓人陶醉~ this is so amazing 。
通過上述的鋪墊引出下面的主角 lamada 表達式實現函數式接口。
爲了可以方便、快捷、幽雅的建立出FI的實例,Java8提供了Lambda表達式這顆語法糖。下面我用一個例子來介紹Lambda語法。假設咱們想對一個List按字符串長度進行排序,那麼在Java8以前,能夠藉助匿名內部類來實現:
List<String> words = Arrays.asList("apple", "banana", "pear");
words.sort(new Comparator<String>() {
@Override
public int compare(String w1, String w2) {
return Integer.compare(w1.length(), w2.length());
}
});
複製代碼
上面的匿名內部類簡直能夠用醜陋來形容,惟一的一行邏輯被五行垃圾代碼淹沒。根據前面的定義(並查看Java源代碼)可知,Comparator是個FI,因此,能夠用Lambda表達式來實現:
words.sort((String w1, String w2) -> {
return Integer.compare(w1.length(), w2.length());
});
複製代碼
ps: 看起來像一個匿名的方法,實際就是一個匿名類對象的引用,代碼看起來更加簡潔。能夠認爲 lambda表達式實現了接口的抽象方法,由於函數式接口默認只有一個抽象方法。