Why doesn't Java allow overriding of static methods?

后端 未结 22 2733
谎友^
谎友^ 2020-11-21 05:49

Why is it not possible to override static methods?

If possible, please use an example.

相关标签:
22条回答
  • 2020-11-21 06:25

    By overriding, you achieve dynamic polymorphism. When you say overriding static methods, the words you are trying to use are contradictory.

    Static says - compile time, overriding is used for dynamic polymorphism. Both are opposite in nature, and hence can't be used together.

    Dynamic polymorphic behavior comes when a programmer uses an object and accessing an instance method. JRE will map different instance methods of different classes based on what kind of object you are using.

    When you say overriding static methods, static methods we will access by using the class name, which will be linked at compile time, so there is no concept of linking methods at runtime with static methods. So the term "overriding" static methods itself doesn't make any meaning.

    Note: even if you access a class method with an object, still java compiler is intelligent enough to find it out, and will do static linking.

    0 讨论(0)
  • 2020-11-21 06:27

    Personally I think this is a flaw in the design of Java. Yes, yes, I understand that non-static methods are attached to an instance while static methods are attached to a class, etc etc. Still, consider the following code:

    public class RegularEmployee {
        private BigDecimal salary;
    
        public void setSalary(BigDecimal salary) {
            this.salary = salary;
        }
    
        public static BigDecimal getBonusMultiplier() {
            return new BigDecimal(".02");
        }
    
        public BigDecimal calculateBonus() {
            return salary.multiply(getBonusMultiplier());
        }
    
        /* ... presumably lots of other code ... */
    }
    
    public class SpecialEmployee extends RegularEmployee {
        public static BigDecimal getBonusMultiplier() {
            return new BigDecimal(".03");
        }
    }
    

    This code will not work as you might expect. Namely, SpecialEmployee's get a 2% bonus just like regular employees. But if you remove the "static"s, then SpecialEmployee's get a 3% bonus.

    (Admittedly, this example is poor coding style in that in real life you would likely want the bonus multiplier to be in a database somewhere rather than hard-coded. But that's just because I didn't want to bog down the example with a lot of code irrelevant to the point.)

    It seems quite plausible to me that you might want to make getBonusMultiplier static. Perhaps you want to be able to display the bonus multiplier for all the categories of employees, without needing to have an instance of an employee in each category. What would be the point of searching for such example instances? What if we are creating a new category of employee and don't have any employees assigned to it yet? This is quite logically a static function.

    But it doesn't work.

    And yes, yes, I can think of any number of ways to rewrite the above code to make it work. My point is not that it creates an unsolvable problem, but that it creates a trap for the unwary programmer, because the language does not behave as I think a reasonable person would expect.

    Perhaps if I tried to write a compiler for an OOP language, I would quickly see why implementing it so that static functions can be overriden would be difficult or impossible.

    Or perhaps there is some good reason why Java behaves this way. Can anyone point out an advantage to this behavior, some category of problem that is made easier by this? I mean, don't just point me to the Java language spec and say "see, this is documented how it behaves". I know that. But is there a good reason why it SHOULD behave this way? (Besides the obvious "making it work right was too hard"...)

    Update

    @VicKirk: If you mean that this is "bad design" because it doesn't fit how Java handles statics, my reply is, "Well, duh, of course." As I said in my original post, it doesn't work. But if you mean that it is bad design in the sense that there would be something fundamentally wrong with a language where this worked, i.e. where statics could be overridden just like virtual functions, that this would somehow introduce an ambiguity or it would be impossible to implement efficiently or some such, I reply, "Why? What's wrong with the concept?"

    I think the example I give is a very natural thing to want to do. I have a class that has a function that does not depend on any instance data, and which I might very reasonably want to call independent of an instance, as well as wanting to call from within an instance method. Why should this not work? I've run into this situation a fair number of times over the years. In practice I get around it by making the function virtual, and then creating a static method whose only purpose in life is to be a static method that passes the call on to the virtual method with a dummy instance. That seems like a very roundabout way to get there.

    0 讨论(0)
  • 2020-11-21 06:29

    The short answer is: it is entirely possible, but Java doesn't do it.

    Here is some code which illustrates the current state of affairs in Java:

    File Base.java:

    package sp.trial;
    public class Base {
      static void printValue() {
        System.out.println("  Called static Base method.");
      }
      void nonStatPrintValue() {
        System.out.println("  Called non-static Base method.");
      }
      void nonLocalIndirectStatMethod() {
        System.out.println("  Non-static calls overridden(?) static:");
        System.out.print("  ");
        this.printValue();
      }
    }
    

    File Child.java:

    package sp.trial;
    public class Child extends Base {
      static void printValue() {
        System.out.println("  Called static Child method.");
      }
      void nonStatPrintValue() {
        System.out.println("  Called non-static Child method.");
      }
      void localIndirectStatMethod() {
        System.out.println("  Non-static calls own static:");
        System.out.print("  ");
        printValue();
      }
      public static void main(String[] args) {
        System.out.println("Object: static type Base; runtime type Child:");
        Base base = new Child();
        base.printValue();
        base.nonStatPrintValue();
        System.out.println("Object: static type Child; runtime type Child:");
        Child child = new Child();
        child.printValue();
        child.nonStatPrintValue();
        System.out.println("Class: Child static call:");
        Child.printValue();
        System.out.println("Class: Base static call:");
        Base.printValue();
        System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
        child.localIndirectStatMethod();
        System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
        child.nonLocalIndirectStatMethod();
      }
    }
    

    If you run this (I did it on a Mac, from Eclipse, using Java 1.6) you get:

    Object: static type Base; runtime type Child.
      Called static Base method.
      Called non-static Child method.
    Object: static type Child; runtime type Child.
      Called static Child method.
      Called non-static Child method.
    Class: Child static call.
      Called static Child method.
    Class: Base static call.
      Called static Base method.
    Object: static/runtime type Child -- call static from non-static method of Child.
      Non-static calls own static.
        Called static Child method.
    Object: static/runtime type Child -- call static from non-static method of Base.
      Non-static calls overridden(?) static.
        Called static Base method.
    

    Here, the only cases which might be a surprise (and which the question is about) appear to be the first case:

    "The run-time type is not used to determine which static methods are called, even when called with an object instance (obj.staticMethod())."

    and the last case:

    "When calling a static method from within an object method of a class, the static method chosen is the one accessible from the class itself and not from the class defining the run-time type of the object."

    Calling with an object instance

    The static call is resolved at compile-time, whereas a non-static method call is resolved at run-time. Notice that although static methods are inherited (from parent) they are not overridden (by child). This could be a surprise if you expected otherwise.

    Calling from within an object method

    Object method calls are resolved using the run-time type, but static (class) method calls are resolved using the compile-time (declared) type.

    Changing the rules

    To change these rules, so that the last call in the example called Child.printValue(), static calls would have to be provided with a type at run-time, rather than the compiler resolving the call at compile-time with the declared class of the object (or context). Static calls could then use the (dynamic) type hierarchy to resolve the call, just as object method calls do today.

    This would easily be doable (if we changed Java :-O), and is not at all unreasonable, however, it has some interesting considerations.

    The main consideration is that we need to decide which static method calls should do this.

    At the moment, Java has this "quirk" in the language whereby obj.staticMethod() calls are replaced by ObjectClass.staticMethod() calls (normally with a warning). [Note: ObjectClass is the compile-time type of obj.] These would be good candidates for overriding in this way, taking the run-time type of obj.

    If we did it would make method bodies harder to read: static calls in a parent class could potentially be dynamically "re-routed". To avoid this we would have to call the static method with a class name -- and this makes the calls more obviously resolved with the compile-time type hierarchy (as now).

    The other ways of invoking a static method are more tricky: this.staticMethod() should mean the same as obj.staticMethod(), taking the run-time type of this. However, this might cause some headaches with existing programs, which call (apparently local) static methods without decoration (which is arguably equivalent to this.method()).

    So what about unadorned calls staticMethod()? I suggest they do the same as today, and use the local class context to decide what to do. Otherwise great confusion would ensue. Of course it means that method() would mean this.method() if method was a non-static method, and ThisClass.method() if method were a static method. This is another source of confusion.

    Other considerations

    If we changed this behaviour (and made static calls potentially dynamically non-local), we would probably want to revisit the meaning of final, private and protected as qualifiers on static methods of a class. We would then all have to get used to the fact that private static and public final methods are not overridden, and can therefore be safely resolved at compile-time, and are "safe" to read as local references.

    0 讨论(0)
  • 2020-11-21 06:30

    I like and double Jay's comment (https://stackoverflow.com/a/2223803/1517187).
    I agree that this is the bad design of Java.
    Many other languages support overriding static methods, as we see in previous comments. I feel Jay has also come to Java from Delphi like me.
    Delphi (Object Pascal) was one of the languages implementing OOP before Java and one of the first languages used for commercial application development.
    It is obvious that many people had experience with that language since it was in the past the only language to write commercial GUI products. And - yes, we could in Delphi override static methods. Actually, static methods in Delphi are called "class methods", while Delphi had the different concept of "Delphi static methods" which were methods with early binding. To override methods you had to use late binding, declare "virtual" directive. So it was very convenient and intuitive and I would expect this in Java.

    0 讨论(0)
  • 2020-11-21 06:30

    Answer of this question is simple, the method or variable marked as static belongs to the class only, So that static method cannot be inherited in the sub class because they belong to the super class only.

    0 讨论(0)
  • 2020-11-21 06:31

    Static methods are treated as global by the JVM, there are not bound to an object instance at all.

    It could conceptually be possible if you could call static methods from class objects (like in languages like Smalltalk) but it's not the case in Java.

    EDIT

    You can overload static method, that's ok. But you can not override a static method, because class are no first-class object. You can use reflection to get the class of an object at run-time, but the object that you get does not parallel the class hierarchy.

    class MyClass { ... }
    class MySubClass extends MyClass { ... }
    
    MyClass obj1 = new MyClass();
    MySubClass obj2 = new MySubClass();
    
    ob2 instanceof MyClass --> true
    
    Class clazz1 = obj1.getClass();
    Class clazz2 = obj2.getClass();
    
    clazz2 instanceof clazz1 --> false
    

    You can reflect over the classes, but it stops there. You don't invoke a static method by using clazz1.staticMethod(), but using MyClass.staticMethod(). A static method is not bound to an object and there is hence no notion of this nor super in a static method. A static method is a global function; as a consequence there is also no notion of polymorphism and, therefore, method overriding makes no sense.

    But this could be possible if MyClass was an object at run-time on which you invoke a method, as in Smalltalk (or maybe JRuby as one comment suggest, but I know nothing of JRuby).

    Oh yeah... one more thing. You can invoke a static method through an object obj1.staticMethod() but that really syntactic sugar for MyClass.staticMethod() and should be avoided. It usually raises a warning in modern IDE. I don't know why they ever allowed this shortcut.

    0 讨论(0)
提交回复
热议问题