电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

为什么匿名内部类参数必须为final类型


发布日期:2021/6/19
 
在线文档阅读开发手记(一)) 从程序设计语言的理论上局部内部类(即定义在方法中的内部类)由于本身就是在方法内部(可出现在形式参数定义处或者方法体处)因而访问方法中的局部变量(形式参数或局部变量)是天经地义的是很自然的

) 为什么JAVA中要加上一条限制只能访问final型的局部变量?

) JAVA语言的编译程序的设计者当然全实现局部内部类能访问方法中的所有的局部变量(因为从理论上这是很自然的要求)但是编译技术是无法实现的或代价极高

) 困难在何处?到底难在哪儿?

局部变量的生命周期与局部内部类的对象的生命周期的不一致性!

) 设方法f被调用从而在它的调用栈中生成了变量i此时产生了一个局部内部类对象inner_object它访问了该局部变量i 当方法f()运行结束后局部变量i就已死亡了不存在了局部内部类对象inner_object还可能 一直存在(只能没有人再引用该对象时它才会死亡)它不会随着方法f()运行结束死亡这时出现了一个荒唐结果局部内部类对象inner_object要访问一个已不存在的局部变量i!

) 如何才能实现?当变量是final时通过将final局部变量复制一份复制品直接作为局部内部中的数据成员这样当局部内部类访问局部变量时其实真正访问的是这个局部变量的复制品(即这个复制品就代表了那个局部变量)因此当运行栈中的真正的局部变量死亡时局部内部类对象仍可以访问局部变量(其实访问的是复制品给人的感觉好像是局部变量的生命期延长了

那么核心的问题是怎么才能使得访问复制品与访问真正的原始的局部变量其语义效果是一样的呢?

当变量是final时若是基本数据类型由于其值不变因而其复制品与原始的量是一样语义效果相同(若不是final就无法保证复制品与原始变量保持一致了因为在方法中改的是原始变量而局部内部类中改的是复制品)

当变量是final时若是引用类型由于其引用值不变(即永远指向同一个对象)因而其复制品与原始的引用变量一样永远指向同一个对象(由于是final从而保证只能指向这个对象再不能指向其它对象)达到局部内部类中访问的复制品与方法代码中访问的原始对象永远都是同一个即语义效果是一样的否则当方法中改原始变量而局部内部类中改复制品时就无法保证复制品与原始变量保持一致了(因此它们原本就应该是同一个变量

一句话这个规定是一种无可奈何也说明程序设计语言的设计是受到实现技术的限制的这就是一例 因为我就看到不少人都持这种观点设计与想法是最重要的实现的技术是无关紧要的只要你作出设计与规定都能实现

现在我们来看如果我要实现一个在一个方法中匿名调用ABSClass的例子

public static void test(final String s){

//或final String s = axman;

ABSClass c = new ABSClass(){

public void m(){

int x = shashCode()

Systemoutprintln(x)

}

};

//其它代码

}

从代码上看在一个方法内部定义的内部类的方法访问外部方法内局部变量或方法参数是非常自然的事但内部类编译的时候如何获取这个变量因为内部类除了它的生命周期是在方法内部其它的方面它就是一个普通类那么它外面的那个局部变量或方法参数怎么被内部类访问?编译器在实现时实际上是这样的

public static void test(final String s){

//或final String s = axman;

class OuterClass$ extends ABSClass{

private final String s;

public OuterClass$(String s){

thiss = s;

}

public void m(){

int x = shashCode()

Systemoutprintln(x)

}

};

ABSClass c = new OuterClass$(s)

//其它代码

}

即外部类的变量被作为构造方法的参数传给了内部类的私有成员

假如没有final那么

public static void test(String s){

//或String s = axman;

ABSClass c = new ABSClass(){

public void m(){

s = other;

}

};

Systemoutprintln(s)

}

就会编译成

public static void test(String s){

//或String s = axman;

class OuterClass$ extends ABSClass{

private String s;

public OuterClass$(String s){

thiss = s;

}

public void m(){

s = other;

}

};

ABSClass c = new OuterClass$ (s)

}

内部类的s重新指向other并不影响test的参数或外部定义的那个s同理如果外部的s重新赋值内部类的s也不会跟着改变

而你看到的

public static void test(String s){

//或String s = axman;

ABSClass c = new ABSClass(){

public void m(){

s = other;

}

};

Systemoutprintln(s)

}

在语法上是一个s在内部类中被改变了但结果打印的出来的你认为是同一的s却还是原来的axman

你能接收这样的结果吗?

所以final从语法上约束了实际上两个不同变量的一致性(表现为同一变量)

上一篇:关于BigDecimal的不精确计算问题

下一篇:在Shell中打开Server Socket