Saturday, December 1, 2012

What is variable hiding and shadowing?


What is variable hiding and shadowing?

In Java, there are three kinds of variables: 
1. local variables, -- in methods
2. instance variables, -- not-static fields
3. class variables. -- static 

Variables have their scopes. Different kinds of variables have different scopes. A variable is shadowed if there is another variable with the same name that is closer in scope. In other words, referring to the variable by name will use the one closest in scope , the one in the outer scope is shadowed.

A Local Variable Shadows An Instance Variable

Inside a class method, when a local variable have the same name as one of the instance variable, the local variable shadows the instance variable inside the method block.
In the following example, there is a instance variable named "a", and inside the method setA() there is a local variable "a":
class Program {
  int  a;
  int  b;

  void setA(int a) {
    a = a; //here, the local variable has closer scope than the instance 
          // variable, so the expression set parameter equal to itself
    this.a = a; // this is the correct way to set the parameter to the 
           //instance variable.
  }

  void setB(int b) {
    this.b = b;
  }
}
Let's look at another example,
class Program {
   String name = "Instance Var."; 
   void someMethod() { 
     String name ="Local Var."; 
     System.out.println(name);   //Local Var.
     System.out.println(this.name); //Instance Var. 
   } 
}
Note: Instance variables are not overridden, they are hidden.

A Class Variable Shadows the Inherited Variable from Its Parent Classes 

When an instance variable in a subclass has the same name as an instance variable in a super class, then the instance variable is chosen in the class that is the reference type.
A class can declare a variable with the same name as an inherited variable from its parent class, thus "hiding" or shadowing the inherited version. (This is like overriding, but for variables.) For example,
public class Base {
    public  String name = "Base";
    public  String getName() { return name; }
}


public class Sub extends Base {
    public String name = "Sub";
    public String getName() { return name; }
}
The above shows two classes and Sub class is a subclass of Base class. Both of classes has a String type variable named name.
  • Use Case I
    class Program {
      public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.name); //Output "Sub"
      }
    }
    The above code directly accesses the name variable in the instance of Sub named s. This access, as one would expect, yields a value of "Sub".
  • Use Case II
    class Program {
      public static void main(String[] args) {
        Sub s  = new Sub();
        Base b = s;
        System.out.println(s.name); //Output "Sub"
        System.out.println(b.name); //Output "Base"
      }
    }
    In use case II, even though the variable b refers an instance of class Sub, the b.name still evaluates to "Base". Because variables names in Java are resolved by the reference type, not the object they are referencing.
    In this example, even though both b and s are referencing the object of type Subs is declared as type Subb is declared as type Base, Java runtime gets the s.name from the class of Sub, and b.name from the class of Base.

Access Supper Class Variables


When shadowing does happen, you can access the super class name by either the syntax super.name or by casting the object to its super class, with the syntax((Superclass)object).name.
Variables shadowed by Interface hierarchies are referenced the same way they are for class hierarchies. The following example shows the shadowing from interface hierarchies:
interface IBase {
  String name = "base";
}

interface ISub extends IBase {
  String name = "sub";
}

public class Shadow implements ISub {
  String name = "shadow";

  void print(Strig name)
  {
    System.out.println("method name = " + name);
    System.out.println("class name = " + this.name);
    System.out.println("IBase name = " + 
          ((IBase) this).name);
    System.out.println("ISub name = " + 
          ((ISub) this).name);
  }

  public static void main(String[] args) {
    (new Shadow()).print("parameter");
  }
}

The output of this program is: 

method name = parameter
name = shadow
IBase name = base
ISub name = sub
As you can see, it can be confusing when variable shadowing occurs. It's a good practice not to use the same variable names in the class or interface hierarchies.
There are exceptions. When the variable in the supper class has private access or is in another package and has default access, there is no room for confusion. The sub class cannot access such variables in its supper class.

No comments:

Post a Comment