標籤:
Java 匿名內部類
前邊一篇文章對匿名內部類做了一個簡單的瞭解,http://my.oschina.net/xinxingegeya/blog/297004。這篇文章來深入的瞭解一下匿名內部類的使用
Java匿名類中的this
ava的匿名類特性,在於可以在項目裡“內聯”地實現一個類型,它可以繼承一個現有的具體或抽象類別,或是實現介面,並提供完整的成員實現。例如,這裡有個抽象類別,定義了一個抽象方法:
// Javaabstract class MyAbstractClass { String getName() { return "MyAbstractClass"; } abstract void print();}
然後我們在另一個地方使用一個匿名類,繼承這個類:
// Javaclass MyClass { String getName() { return "MyClass"; } MyAbstractClass someMethod() { return new MyAbstractClass() { public void print() { System.out.println(getName()); } }; }}
好,現在提一個問題,運行下面這行代碼會列印出什麼結果?
// Javanew MyClass().someMethod().print();
輸出結果是MyAbstractClass而不是MyClass。換句話說,匿名型別中調用的getName方法是定義在MyAbstractClass裡的,而不是定義在詞法範圍(Lexical Scope)裡的getName方法。根據Java規範,匿名類中的this(包括上面代碼中“隱式”的this)表示類型本身對象,而與上下文無關。如果要訪問詞法範圍裡的getName方法(即MyClass的方法),則反而必須顯式指定MyClass類 ,例如:
// Javaclass MyClass { String getName() { return "MyClass"; } MyAbstractClass someMethod() { return new MyAbstractClass() { public void print() { System.out.println(MyClass.this.getName()); } }; }}
匿名內部類定義屬性欄位
如下是匿名內部類的例子,
package com.usoft.java;/** */public interface CallBack { void call();}
package com.usoft.java;/** * @author: Lenovo(2015-08-09 16:00) */public class Test { private final Integer c = 12; private Integer a; private Integer b; public Test(Integer a, Integer b) { this.a = a; this.b = b; } public static void main(String args[]) { Test test = new Test(1, 2); test.test(true); } public void hello(CallBack back) { back.call(); } public void test(final boolean flag) { System.out.println("test"); hello(new CallBack() { private Integer ia = a; private Integer ib = b; @Override public void call() { System.out.println("callback"); if (flag) System.out.println(ia + ib + c); else System.out.println(ia + ib + c + 1); } }); }}
匿名內部類雖然說是匿名,但編譯後也是有名字的。看一下匿名內部類的反編譯結果,類的名字是 Test$1,和普通的類沒有什麼區別。
C:\WorkSpace5-gitosc\scala-sample\out\production\scala-sample\com\usoft\java>javap -p Test$1.classCompiled from "Test.java"class com.usoft.java.Test$1 implements com.usoft.java.CallBack { private java.lang.Integer ia; private java.lang.Integer ib; final boolean val$flag; final com.usoft.java.Test this$0; com.usoft.java.Test$1(com.usoft.java.Test, boolean); public void call();}
在匿名內部類中定義了兩個屬性 ia 和 ib,ia 和 ib 的初始化和賦值是在匿名內部類的建構函式中完成的,值是外部類中a 和 b。在匿名內部類中使用了外部的變數 flag。
其中初始化和賦值 ia 和 ib的時候更明顯和更好理解的寫法是,
public void test(final boolean flag) { System.out.println("test"); hello(new CallBack() { private Integer ia = Test.this.a; private Integer ib = Test.this.b; @Override public void call() { System.out.println("callback"); if (flag) System.out.println(ia + ib + c); else System.out.println(ia + ib + c + 1); } });}
運行結果,
test
callback
15
匿名內部類的初始化
由於匿名內部類沒有建構函式(通過反編譯你可以知道是有建構函式的),那麼怎麼初始化匿名內部類,下面是一種方法,
public void test(final boolean flag) { System.out.println("test"); hello(new CallBack() { private Integer ia = Test.this.a; private Integer ib = Test.this.b; { ia = 3; ib = 4; } @Override public void call() { System.out.println("callback"); if (flag) System.out.println(ia + ib + c); else System.out.println(ia + ib + c + 1); } });}
通過代碼塊,可以初始化類的屬性,列印的結果為 19;
為什麼使用final
通過上面的反編譯結果,我們可以看到,內部類的建構函式(位元組碼層面)中有一個外部類參數,同時內部類中有一個外部類的引用,當使用外部類的屬性時,是通過外部類的引用得到的相應的屬性值。如果外部類的屬性引用改變,那麼內部類和外部類狀態就不統一了。所以為了狀態的統一,規定所有的外部類的屬性為final定義,即不可改變的引用。
private final Integer c = 12; // 必須為final
匿名內部類的使用總結
使用匿名內部類時,我們必須是繼承一個類或者實現一個介面,但是兩者不可兼得,同時也只能繼承一個類或者實現一個介面。
匿名內部類中是不能定義建構函式的。
匿名內部類中不能存在任何的靜態成員變數和靜態方法。
匿名內部類為局部內部類,所以局部內部類的所有限制同樣對匿名內部類生效。
匿名內部類不能是抽象的,它必須要實現繼承的類或者實現的介面的所有抽象方法。
參考引用:http://www.cnblogs.com/chenssy/p/3390871.html
http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html
=======================END=======================
Java 匿名內部類