标签:角度 举例 局部变量 ctf runnable tab long outer 社区
中英文社区中,比较常见的对闭包的定义是
引用了自由变量的一段代码或函数,被引用的自由变量和函数(一段代码)共同存在,即使离开了创造它的环境
按照我的理解,scala/java中虽然并不存在语法级地支持或是定义,对于闭包而言,一些概念和闭包的概念一致。一般理解scala中的一些概念,我会倾向于从Java开始。
在java中,内部类有:
成员内部类
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;
}
}
}
拿以上代码举例,成员内部类可以访问到外部类中的所有字段、方法,包括私有。
内部类的实现均是通过编译器构造字节码实现的。上述类经过编译产生的类大概如下
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的对外部对象的引用。
静态内部类
静态内部类并不具有内部类和外部类之间的依赖关系,静态内部类和在一个文件中写两个类没啥却别,一般用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;
}
}
}
}
上述代码构造出的类如下:
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
的操作,就是这个原理。
匿名内部类
匿名内部类和局部内部类没有太大区别,只是生成的类的类名不含用户的标识。
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
标签:角度 举例 局部变量 ctf runnable tab long outer 社区
原文地址:https://www.cnblogs.com/lshao/p/12919922.html