变量遮盖

原文: https://javabeginnerstutorial.com/core-java-tutorial/variable-shadowing/

今天,我们将探讨 Java 的一项罕见功能:变量遮盖

首先,让我们定义什么是遮盖字段或方法:

被遮盖时,该字段被视为

  • 其声明类的子类声明一个具有同名的字段
  • 在本地范围内声明具有相同名称和类型的变量
  • 用相同名称和类型声明方法参数/参数

局部变量遮盖

  1. public class MyClass
  2. {
  3. private int count = 10;
  4. private void localVariable()
  5. {
  6. int count = 5;
  7. System.out.println("count = "+ count);
  8. }
  9. public static void main(String[] args)
  10. {
  11. MyClass test = new MyClass();
  12. test.localVariable();
  13. }
  14. }

上面的代码将输出

count=5

因为在第 7 行中声明的count局部变量遮盖了在类级别声明的变量计数。 如果要访问实例变量,则需要添加this关键字。

  1. private void localVariable()
  2. {
  3. int count = 5;
  4. System.out.println("count = "+ this.count);
  5. }

方法参数遮盖

即使我们对此不太关注,这种情况也很常见。 下面是一个简单的获取器定义

  1. private int count;
  2. public void setCount(int count)
  3. {
  4. this.count = count;
  5. }

this关键字是解决歧义所必需的。 如果没有this,编译器将无法知道我们是否要为其自身分配计数方法参数值。 如果删除this关键字,则仍然会收到编译警告。

超类字段遮盖

让我们考虑以下类:

  1. public class SuperClass
  2. {
  3. protected String val = "SUPER_VAL";
  4. protected void display()
  5. {
  6. System.out.println("val = "+this.val);
  7. }
  8. }
  9. public class ChildClass extends SuperClass
  10. {
  11. private String val;
  12. public ChildClass(String value) {
  13. this.val = value;
  14. }
  15. public static void main(String[] args)
  16. {
  17. ChildClass child = new ChildClass("CHILD_VAL");
  18. child.display();
  19. }
  20. }

执行给出:

value = SUPER_VAL

SuperClass中声明了val字段,但在ChildClass中被遮盖了,因为后者声明了另一个具有相同名称和类型的字段。 尽管已使用CHILD_VAL实例化了ChildClass,但执行child.display()却给您SUPER_VAL

原因很简单。 创建子实例时,有 2 个变量val。 通过构造器,来自SuperClass的一个具有值SUPER_VAL,来自ChildClass的一个具有注入值CHILD_VAL

调用display()方法时,由于它是在超类中定义的,因此它是超类的上下文中的val字段。输出显示SUPER_VAL也就不足为奇了。

  1. public class ChildClass extends SuperClass
  2. {
  3. private String val;
  4. public ChildClass(String value) {
  5. this.val = value;
  6. super.val = value;
  7. }
  8. public static void main(String[] args)
  9. {
  10. ChildClass child = new ChildClass("CHILD_VAL");
  11. child.display();
  12. }
  13. }

在上面修改的代码中,我们将SuperClass中隐藏的val字段的值强制为super.val = value,输出给出:

value = CHILD_VAL

现在让我们在层次结构中添加另一个类

  1. public class AncestorClass
  2. {
  3. protected String val = "ANCESTOR_VAL";
  4. }
  5. public class SuperClass extends AncestorClass
  6. {
  7. protected String val = "SUPER_VAL";
  8. }
  9. public class ChildClass extends SuperClass
  10. {
  11. private String val = "CHILD_VAL";
  12. public void displayVal()
  13. {
  14. System.out.println("val = " + super.val);
  15. }
  16. public static void main(String[] args)
  17. {
  18. ChildClass child = new ChildClass();
  19. child.displayVal();
  20. }
  21. }

显然,输出将显示

val = SUPER_VAL

现在的问题是:如果要显示祖先类的val值,该怎么办? 显然,仅super关键字引用了类层次结构中的第一个父类。

救援人员来了。 实际上,我们可以将代表当前类实例的关键字强制应用于类层次结构中的特定类型!

  1. public class ChildClass extends SuperClass
  2. {
  3. private String val = "CHILD_VAL";
  4. public void displayVal()
  5. {
  6. System.out.println("val = " + ((AncestorClass) this).val);
  7. }
  8. public static void main(String[] args)
  9. {
  10. ChildClass child = new ChildClass();
  11. child.displayVal();
  12. }
  13. }

这次,我们确实有

val = ANCESTOR_VAL