中英文社区中,比较常见的对闭包的定义是
引用了自由变量的一段代码或函数,被引用的自由变量和函数(一段代码)共同存在,即便离开了创造它的环境
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