When a class declares a static method m, the declaration of m hides any method m', where the signature of m is a subsignature of the signature of m' and the declaration of m' is both in the superclasses and superinterfaces of the declaring class and also would otherwise be accessible to code in the declaring class (The Java Language Specification, §8.4.8.2 "Hiding (by Class Methods)" [JLS 2015]).

An instance method defined in a subclass overrides another instance method in the superclass when both have the same name, number and type of parameters, and return type.

Hiding and overriding differ in the determination of which method is invoked from a call site. For overriding, the method invoked is determined at runtime on the basis of the specific object instance in hand. For hiding, the method invoked is determined at compile time on the basis of the specific qualified name or method invocation expression used at the call site. Although the Java language provides unambiguous rules for determining which method is invoked, the results of these rules are often unexpected. Additionally, programmers sometimes expect method overriding in cases where the language provides method hiding. Consequently, programs must never declare a class method that hides a method declared in a superclass or superinterface.

Noncompliant Code Example

In this noncompliant code example, the programmer hides the static method rather than overriding it. Consequently, the code invokes the displayAccountStatus() method of the superclass, causing it to print  Account details for admin despite being instructed to choose user rather than admin.

class GrantAccess {
  public static void displayAccountStatus() {
    System.out.println("Account details for admin: XX");
  }
}

class GrantUserAccess extends GrantAccess {
  public static void displayAccountStatus() {
    System.out.println("Account details for user: XX");
  }
}

public class StatMethod {
  public static void choose(String username) {
    GrantAccess admin = new GrantAccess();
    GrantAccess user = new GrantUserAccess();
    if (username.equals("admin")) {
      admin.displayAccountStatus();
    } else {
      user.displayAccountStatus();
    }
  }

  public static void main(String[] args) {
    choose("user");
  }
}

Compliant Solution

In this compliant solution, the programmer declares the displayAccountStatus() methods as instance methods by removing the static keyword. Consequently, the dynamic dispatch at the call sites produces the expected result. The @Override annotation indicates intentional overriding of the parent method.

class GrantAccess {
  public void displayAccountStatus() {
    System.out.print("Account details for admin: XX");
  }
}

class GrantUserAccess extends GrantAccess {
  @Override
  public void displayAccountStatus() {
    System.out.print("Account details for user: XX");
  }
}

public class StatMethod {
  public static void choose(String username) {
    GrantAccess admin = new GrantAccess();
    GrantAccess user = new GrantUserAccess();

    if (username.equals("admin")) {
      admin.displayAccountStatus();
    } else {
      user.displayAccountStatus();
    }
  }

  public static void main(String[] args) {
    choose("user");
  }
}

The methods inherited from the superclass can also be overloaded in a subclass. Overloaded methods are new methods unique to the subclass and neither hide nor override the superclass method [Java Tutorials].

Technically, a private method cannot be hidden or overridden. There is no requirement that private methods with the same signature in the subclass and the superclass bear any relationship in terms of having the same return type or throws clause, the necessary conditions for hiding [JLS 2015]. Consequently, hiding cannot occur when private methods have different return types or throws clauses.

Exceptions

MET07-J-EX0: Occasionally, an API provides hidden methods. Invoking those methods is not a violation of this rule provided that all invocations of hidden methods use qualified names or method invocation expressions that explicitly indicate which specific method is invoked. If the displayAccountStatus() were a hidden method, for example, the following implementation of the choose() method would be an acceptable alternative:

  public static void choose(String username) {
    if (username.equals("admin")) {
      GrantAccess.displayAccountStatus();
    } else {
      GrantUserAccess.displayAccountStatus();
    }
  }

Risk Assessment

Confusing overriding and hiding can produce unexpected results.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MET07-J

Low

Unlikely

Medium

P2

L3

Automated Detection

Automated detection of violations of this rule is straightforward. Automated determination of cases in which method hiding is unavoidable is infeasible. However, determining whether all invocations of hiding or hidden methods explicitly indicate which specific method is invoked is straightforward.

ToolVersionCheckerDescription
Parasoft Jtest
2023.1
CERT.MET07.AHSMDo not hide inherited "static" member methods

Bibliography



11 Comments

  1. Could use more NCCE/CCE pairs. One should demonstrate a private method being hidden (not overridden) by an identical method in a subclass.

    1. While it is certainly permissible to have a private method as hidden in a subclass, would it result in any vulnerability? For one, main() or anyone who instantiates the objects would not be able to invoke either methods as they would be invisible. If it does result in some unexpected behavior, I can add it as a separate example. Thanks.

      1. The overridden method must also be accessible to the override method. Private methods are never overriden even using nested classes. This is different from C++, where private "template functions" can be overriden. Default access (package-private) methods may not be overriden by classes outside of the package. Exception specifications are not considered by the verifier, unlike javac.

        1. I agree. According to JLS section 8.4.6.3 (Requirements in Overriding and Hiding) -

          "Note that a private method cannot be hidden or overridden in the technical sense of those terms. This means that a subclass can declare a method with the same signature as a private method in one of its superclasses, and there is no requirement that the return type or throws clause of such a method bear any relationship to those of the private method in the superclass."

          So technically the subclass method has no relationship with the superclass method and thus it is not the case of "hiding" either.

  2. The NCCE/CS pair seems to be indicating that static functions in derived classes hide, rather than override, static functions in base classes.

    I'll grant that private methods cannot be hidden or overridden. However, I still wish this rec had more NCCE/CS pairs to demonstrate overriding vs. hiding. Is there any other way to hide base methods in Java?

    C++ has the rec VOID OOP02-CPP. Do not hide inherited non-virtual member functions. Since Java lacks 'non-virtual' members, this would not make a good Java rule.

    1. In addition, hiding is allowed for variables but this guideline talks about overriding vs hiding so it won't fit. Currently, I am unaware of other examples.

      I took a peek at the C++ rule and it seems to be suggesting that hiding should be avoided as it causes confusion (our friendly programmer expected overriding to occur). IIRC, hiding methods is usually not considered too sinful (@Override before an overridden method should also help clarify the intent), though hiding fields is not recommended.

  3. Reworded to make the rule normative. We now say "don't do it", but permit a limited exception for the few cases where hiding is required. Furthermore, all but the "hiding is required" part appear easy to check in a tool.

    1. I find the exception somewhat confusing. It relates to client behavior, but the rule relates to API-developer behavior.

  4. A static method (class method) cannot be overridden in Java but if a static method defined in the parent class is redefined in a child class, the child class's method hides the method defined in the parent class. This mechanism is called method hiding in Java or function hiding.

  5. Even though this rule seems a good catch, nowadays IDEs provide hints that static methods and attributes should be called by the class and not by an instance, i.e.

    if (username.equals("admin")) {
        GrantAccess.displayAccountStatus();
    } else {
        GrantUserAccess.displayAccountStatus();
    }

    as static methods and attributes belong to the class not to an instance.

    1. You might have an espacially vigilant IDE, but we have to assume that some people have nothing more than their compiler and a bare-bones text editor, which will not catch such errors.