OCA Java SE 8 Class Design - Java Method Hiding








Hiding Static Methods

A hidden method occurs when a child class defines a static method with the same name and signature as a static method from parent class.

The following list summarizes the five rules for hiding a method:

The method from child class must have the same signature as the method from parent class.

The method from the child class must be at least as accessible or more accessible than the method from the parent class.

The method from the child class may not throw a checked exception that is new or broader than the class of any exception thrown from the parent class method.

If the method returns a value, it must be the same or a subclass of the method from the parent class, known as covariant return types.

The method defined in the child class must be marked as static if it is marked as static in the parent class (method hiding).

The method must not be marked as static from the child class if it is not marked as static in the parent class (method overriding).

Note that the first four are the same as the rules for overriding a method.

class Shape { 
  public static void print() { 
    System.out.println("Shape is printing"); 
  } 
} 

class Rectangle extends Shape { 
  public static void print() { 
    System.out.println("Rectangle is printing"); 
  } 
  public static void main(String[] args) { 
    Rectangle.print(); 
  } 
} 

The code above compiles and runs without issue.

The print() method in the child class hides the print() method in the parent class. Because they are both marked as static, this is not considered an overridden method.





Example

The following code violates the fifth rule:

class Shape { 
  public static void resize() { 
    System.out.println("Shape is resizing"); 
  } 
  public void print() { 
    System.out.println("Shape is printing"); 
  } 
} 

class Rectangle extends Shape { 
  public void resize() {  // DOES NOT COMPILE 
     System.out.println("Rectangle resizes quietly"); 
  } 
  public static void print() {  // DOES NOT COMPILE 
     System.out.println("Rectangle is going to print"); 
  } 
} 

resize() is marked as static in the parent class but not in the child class.





Overriding vs. Hiding Methods

With Overriding a method a child method replaces the parent method in calls defined in both the parent and child.

Hidden methods only replace parent methods in the calls defined in the child class.

The child version of an overridden method is executed for an instance regardless of whether the method call is defined in a parent or child class method.

The parent method is never used unless an explicit call to the parent method is referenced, using the syntax ParentClassName.method().

At runtime the parent version of a hidden method is always executed if the call to the method is defined in the parent class.

Let's take a look at an example:

public class Shape { 
  public static boolean isPrinted() { 
    return false; 
  } 
  public void getShapeDescription() { 
    System.out.println("Shape: "+isPrinted()); 
  } 
} 

public class Rectangle extends Shape { 
  public static boolean isPrinted() { 
    return true; 
  } 
  public void getRectangleDescription() { 
    System.out.println("Rectangle: "+isPrinted()); 
  } 
  public static void main(String[] args) { 
    Rectangle r = new Rectangle(); 
    r.getShapeDescription(); 
    r.getRectangleDescription(); 
  } 
} 

isPrinted() returns false in the parent class and true in the child class.

In the first method call, the parent method getShapeDescription() is used. The Shape class only knows about isPrinted() from its own class definition, so it outputs false.

In the second method call, the child executes a method of isPrinted(), which hides the parent method's version and returns true.

Example 2

The following example uses an overridden version of isPrinted() instead of a hidden version:

class Shape { 
  public boolean isPrinted() { 
    return false; 
  } 
  public void getShapeDescription() { 
    System.out.println("Shape: "+isPrinted()); 
  } 
} 

public class Rectangle extends Shape { 
  public boolean isPrinted() { 
    return true; 
  } 
  public void getRectangleDescription() { 
    System.out.println("Rectangle: "+isPrinted()); 
  } 
  public static void main(String[] args) { 
    Rectangle r = new Rectangle(); 
    r.getShapeDescription(); 
    r.getRectangleDescription(); 
  } 
} 

This code also compiles and runs without issue.

isPrinted() method is overridden, not hidden, in the child class. It is replaced at runtime in the parent class with the call to the child class's method.

final methods

final methods cannot be overridden.

This rule is in place both when you override a method and when you hide a method.

In other words, you cannot hide a static method in a parent class if it is marked as final.

class Shape { 
  public final boolean hasPrinted() { 
    return true; 
  } 
} 

class Rectangle extends Shape { 
  public final boolean hasPrinted() { // DOES NOT COMPILE 
    return false; 
  } 
} 

In this example, hasPrinted() is marked as final in the parent class Shape, so the child class Rectangle cannot override the parent method, resulting in a compiler error.

Inheriting Variables

Java doesn't allow variables to be overridden but instead hidden.

To hide a variable, you can define a variable with the same name as a variable in a parent class.

As when hiding a static method, you can't override a variable and you can only hide it.

If you're referencing the variable from within the parent class, the variable defined in the parent class is used.

If you're referencing the variable from within a child class, the variable defined in the child class is used.

You can reference the parent value of the variable with an explicit use of the super keyword.

Consider the following example:

class Shape { 
  protected int edgeLength = 4; 
  public void getShapeDetails() { 
    System.out.println("[parentTail="+edgeLength+"]"); 
  } 
} 

class Main extends Shape { 
  protected int edgeLength = 8; 
  public void getMainDetails() { 
    System.out.println("[tail="+edgeLength +",parentTail="+super.edgeLength+"]"); 
  } 
  public static void main(String[] args) { 
    Main mouse = new Main(); 
    mouse.getShapeDetails(); 
    mouse.getMainDetails(); 
  } 
} 

Main contains two copies of the edgeLength variables: one defined in the parent and one defined in the child.

These instances are kept separate from each other and our instance of Main to reference both edgeLength values independently.

In the first method call, getShapeDetails(), the parent method outputs the parent value of the edgeLength variable.

In the second method call, getMainDetails(), the child method outputs both the child and parent version of the edgeLength variables, using the super keyword to access the parent variable's value.