引言
Java 中的局部类(Local Class,本地类)和匿名类(Anonymous Class)都存在变量隐藏(Shadowing)。
当我们理解变量隐藏之后,将可以更好地理解 Lambda 表达式作用域,因为 Lambda 表达式没有任何的变量隐藏。
什么是变量隐藏
要理解什么是变量隐藏,最好的方式就是通过代码去理解,譬如以下代码:
package com.wuxianjiezh.test;
public class ScopeTest {
interface A {
public void print(int x);
}
public int x = 0;
class InnerClass {
// 内部类 `InnerClass` 的成员变量 `x` 隐藏了封闭类 `ScopeTest` 的成员变量 `x`
public int x = 1;
// `print` 方法的参数 `x` 隐藏了内部类 `InnerClass` 的成员变量 `x`
void print(int x) {
// 引用了参数 `x`
System.out.println("x = " + x);
// 使用 `this` 引用内部类的成员变量
System.out.println("this.x = " + this.x);
// 使用变量所属的类名来引用较大范围的成员变量
System.out.println("ScopeTest.this.x = " + ScopeTest.this.x);
}
}
void print(int x) {
A a = new A() {
// 匿名类的成员变量 `x` 隐藏了封闭类 `ScopeTest` 的成员变量 `x`
public int x = 1;
// `print` 方法的参数 `x` 隐藏了匿名类的成员变量 `x`
@Override
public void print(int x) {
// 引用了参数 `x`
System.out.println("x = " + x);
// 使用 `this` 引用匿名类的成员变量
System.out.println("this.x = " + this.x);
// 使用变量所属的类名来引用较大范围的成员变量
System.out.println("ScopeTest.this.x =x " + ScopeTest.this.x);
}
};
a.print(x);
}
public static void main(String... args) {
ScopeTest scopeTest = new ScopeTest();
InnerClass innerClass = scopeTest.new InnerClass();
innerClass.print(21);
scopeTest.print(22);
}
}
以上代码的输出结果为:
x = 21
this.x = 1
ScopeTest.this.x = 0
x = 22
this.x = 1
ScopeTest.this.x =x 0
总结
如果特定范围(如内部类或方法定义)的类型声明(如成员变量或参数)的名称与封闭范围中的另一个声明具有相同的名称,则特定范围的声明将隐藏(shadow)封闭范围的声明。
注意:正如我们在 Java 变量捕获(Captured Variable)一文最后总结部分所说的,匿名类除了没有名字外,其它和局部类并没有什么不同。因此如果没有特殊说明,当我们说内部类如何如何时,隐性也指明了匿名类。