Search code examples
javaoverridingredefinemethod-hidingredeclare

Overriding, redeclaring, hiding and redefining of methods in classes, interfaces and classes implementing interfaces


Would it be correct to say that static methods in a class extended with a class, in an interface extended with an interface and in an interface implemented by a class can only be redeclared in the extending/implementing part (hiding the original method in the class extending class case), and any other method can only be overridden there? And is redefining the same as overriding?

If not, can someone please explain these concepts with a flowchart (I've looked at other explanations and they don't give me the overview I'm looking for)?


Solution

  • Would it be correct to say that static methods in a class extended

    No. static methods fundamentally are un-inheritable, and un-overridable. Or rather, conceptually it just doesn't apply.

    Saying: "This static method is an override" is a bit like stating: "This smells blue" - it's not clear what that would even mean.

    overriding is relevant essentially only for the notion of dynamic dispatch. Dynamic dispatch is this idea:

    class Dog {
      void bark() { System.out.println("Woof"); }
    }
    
    class Bulldog extends Dog {
      void bark() { System.out.println("Grrrr"); }
    }
    
    Dog d = new Bulldog();
    d.bark(); // prints "Grrrr"
    

    That's dynamic dispatch at work. d is a variable. Like all non-primitives in java, it is a reference. Meaning, values stored in this variable are the reference - a pointer. An address in an addressbook that lets you get to a house, not the house itself. d's type is Dog. Its value is a reference to an actual instance of some object (and java guarantees that whatever the actual type it is an instance of, that type is Dog or a subtype of Dog). That reference gets you an instance of Bulldog. So, when invoking bark() here, what happens?

    Dynamic dispatch: Java finds the most specific implementation of this method, and calls that. So that prints Grrrr, and not Woof.

    However, when talking about static methods, the entire concept doesn't apply. Dynamic dispatch occurs because there is a discrepancy between the type of the reference (which is Dog here, that's the type of variable d) and the type of the thing the reference is pointing at (which is Bulldog). Overriding as a concept exists because these 2 types may not be the same.

    When invoking static methods, this doesn't come up. You always write SomeType.someStaticMethod(), so what would dynamic dispatch even be?

    SIDENOTE: You can legally invoke static methods using an expression. This is legal:

    List.of("a", "b"); // this is how you normally do it
    List<Integer> list = new ArrayList<Integer>();
    list.of("a", "b"); // this compiles and works
    

    But make no mistake - the compiler treats it the same and cares only about the type of list, not about the object the variable is pointing at. In fact, list = null; list.of("a", "b"); works fine, no NullPointerException, proving the point that list doesn't get dereferenced when you do this. All style guides strongly recommend not doing this, for good reason.

    SIDENOTE 2: Unfortunately java lets you declare a static method as final which is weird: final means: Cannot be overridden, and we just figured out that 'override' conceptually doesn't make sense for static methods. It's just what it is - it's a design error in the original java spec and java does not like making changes that break existing code unless there is an incredibly good reason to do so, and whilst this spec brainfart is annoying, it doesn't hurt much. Just don't declare your static methods final. It even adds a rule (subtypes cannot declare a static method with the same signature) which is even more bizarre. Again, disregard that - java spec error that doesn't get fixed because not worth the headache.

    And is redefining the same as overriding?

    Yes. 2 words for the same thing. The correct term is 'override' - both the java lang spec uses this term, and the vast majority of the community does. If I was an editor of whatever tutorial, blogpost, documentation, or presentation you got this 'redefine' term from, I'd edit it. Unless the author was specifically talking about this:

    class Parent {
      static void foo() {}
    }
    
    class Child extends Parent {
      static void foo() {}
    }
    

    In which case 'override' is the wrong term to use (given that static methods and override are orthogonal concepts), though I wouldn't use 'redefine' either. You're just defining a method named foo in one class, and also defining a method named foo in another. The fact that one is a child of the other is irrelevant; both have a foo method now. The foo method in child does not override or redefine anything; there's no such thing in static world.

    with a flowchart

    A flowchart requires some sort of flow to chart. Hence the name. No such thing here.