中英文社區中,比較常見的對閉包的定義是
引用了自由變量的一段代碼或函數,被引用的自由變量和函數(一段代碼)共同存在,即便離開了創造它的環境
java
按照個人理解,scala/java中雖然並不存在語法級地支持或是定義,對於閉包而言,一些概念和閉包的概念一致。通常理解scala中的一些概念,我會傾向於從Java開始。git
在java中,內部類有:github
成員內部類閉包
class Outer1{ private int a1; private static int s1; void f1() { } class Inner1{ int a2; void f2(){ //access outer's field,function int b=a1; //能夠直接引用或是Outer1.this.a1; Outer1.this.f1(); int c=Outer1.s1; } } }
拿以上代碼舉例,成員內部類能夠訪問到外部類中的全部字段、方法,包括私有。
內部類的實現均是經過編譯器構造字節碼實現的。上述類通過編譯產生的類大概以下app
class Outer1{ private int a1; private static int s1; void f1() { } static int access$000(Outer1 outer){ return outer.a1; } int access$100(){ return a1; } } class Inner1{ int a1; final Outer1 this$0; Inner1(Outer1 outer){ this.this$0=outer; } void f2(){ int b=Outer1.access$000(this$0); this$0.f1(); int c=Outer1.access$100(); } }
能夠看到,在外部類中添加了相應的方法,給內部類調用來達到訪問外部類的private成員或是方法的目的。在內部類中,會添加一個this$0的對外部對象的引用。ide
靜態內部類
靜態內部類並不具備內部類和外部類之間的依賴關係,靜態內部類和在一個文件中寫兩個類沒啥卻別,通常用privat static內部類來隱藏實現。函數
class SomeInterface{ void function(); } class SomeInterfaceFactory{ private static class SomeInterfaceImpl implements SomeInterface{ } static newInstance(){ return new SomeInterfaceImpl() } }
局部內部類
內部類能夠寫在函數中,除了外部類的變量和方法外,內部類還能夠訪問到函數中的局部變量.測試
class Outer3{ int a1; void function(){ int used=0; int notUsed=-1; class Inner3{ void f2(){ int t1=used; int t2=a1; } } } }
上述代碼構造出的類以下:this
class Outer3{ int a1; void function(){ int used=0; int notUsed=-1; } } class Inner3{ final int val$used; //從這裏看出不能對外部變量賦值 final Outer3 this$0; Inner3(Outer3 outer,int a){ this.this$0=outer; this.val$used=a } void f2(){ int t1=val$used; int t2=this$0.a1; } }
從上面能夠看出,局部內部類除了像成員內部類那樣添加了外部對象的引用,還添加了對引用到的局部變量的引用,而且這些屬性會經過構造函數進行初始化。
此外,在Inner3的f2中,不能執行相似used=10
的操做,是由於這些引用是final的,固然,對於對象類型,對象內部仍是能夠修改的,scala中的局部內部類能夠更改執行相似used=10
的操做,就是這個原理。spa
匿名內部類
匿名內部類和局部內部類沒有太大區別,只是生成的類的類名不含用戶的標識。
class Outer4{ int a1; void function(){ int used=0; int notUsed=-1; Runnable t=new Runnable() { @Override public void run() { int t1=used; int t2=a1; } }; } }
上述代碼構造出的類以下:
class Outer4{ int a1; void function(){ int used=0; int notUsed=-1; Runnable t=new Outer4$1(); } } class Outer4$1 implements java.lang.Runnable{ final int val$used; final Outer4 this$0; public void run(){ //... } }
總結
除靜態內部類外,java編譯器會作如下事情:
scala中,內部類的實現與java基本一致,其中函數實現相似實現AbstractFunction接口的的匿名內部類。
成員內部類
成員內部類與java基本一致,對private屬性的處理稍微有些不一樣,在scala中,其實全部成員變量均是private的,編譯器自動爲其添加相應的getter/setter,所以若是內部類訪問了外部類的私有屬性,則編譯器只需調整相應的getter的訪問權限。
局部內部類
局部內部類也與java基本一致,只是局部內部類中能夠修改外部的局部變量。其實現原理也很簡單,在局部變量外再包一層,如int改成IntRef,好比
final int[] data=new int[1] data[0]=1 //set int tmp=data[0] //get
匿名內部類
scala中,除了像java那樣定義匿名內部類外,函數實現也是匿名內部類實現。如函數
class Outer4{ private val a1=-1 val f1: Int => Int = (a: Int) => a + 1 val f2: Int => Int = (a: Int) => a + a1 }
會生成相似以下匿名內部類
//對應f1 public final class Outer4$$anonfun$3 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable { public static final long serialVersionUID=0L; public final int apply(int){ //計算邏輯 } public Outer4$$anonfun$3(Outer4 outer){ //... } } //對應f1 public final class Outer4$$anonfun$3 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable { public static final long serialVersionUID=0L; private final Outer4 $outer; public final int apply(int){ //計算邏輯 } public Outer4$$anonfun$3(Outer4 outer){ //... } }
從上面的例子來看,最大的不一樣是外部對象引用的不一樣,在某些狀況下一個匿名內部類可能僅僅是'匿名類',經過測試驗證,發現僅在如下狀況下內部類會添加對外部類的引用:
還有一個問題,若是在函數A內部定義了函數B,函數B訪問了函數A的局部變量,則函數B的匿名內部類會添加函數A的匿名內部類的引用嗎?
class Outer4{ val a1=-1 val fa=()=>{ val local=a1 val fb=()=>{ println(local) } val fc=()=>{ println(a1) } } }
答案是否認的。
在以上代碼中,fb不會持有外部對象即fa的引用,fb對local引用被看成局部變量處理,
這和上面java例子中Inner3對used變量的訪問一致。
fc會持有外部對象即fa的引用,這是由於fc訪問了a1,至關於fa.outer.a1
.
總結
輪子 | 版本 |
---|---|
java | 1.8 |
scala | 2.11.12 |
spark | 2.2.0 |
閉包
https://github.com/ColZer/DigAndBuried/blob/master/spark/function-closure-cleaner.md