Why doesn't Java allow overriding of static methods?

后端 未结 22 2691
谎友^
谎友^ 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:10

    overriding is reserved for instance members to support polymorphic behaviour. static class members do not belong to a particular instance. instead, static members belong to the class and as a result overriding is not supported because subclasses only inherit protected and public instance members and not static members. You may want to define an inerface and research factory and/or strategy design patterns to evaluate an alternate approach.

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

    In Java (and many OOP languages, but I cannot speak for all; and some do not have static at all) all methods have a fixed signature - the parameters and types. In a virtual method, the first parameter is implied: a reference to the object itself and when called from within the object, the compiler automatically adds this.

    There is no difference for static methods - they still have a fixed signature. However, by declaring the method static you have explicitly stated that the compiler must not include the implied object parameter at the beginning of that signature. Therefore, any other code that calls this must must not attempt to put a reference to an object on the stack. If it did do that, then the method execution would not work since the parameters would be in the wrong place - shifted by one - on the stack.

    Because of this difference between the two; virtual methods always have a reference to the context object (i.e. this) so then it is possible to reference anything within the heap that belong to that instance of the object. But with static methods, since there is no reference passed, that method cannot access any object variables and methods since the context is not known.

    If you wish that Java would change the definition so that a object context is passed in for every method, static or virtual, then you would in essence have only virtual methods.

    As someone asked in a comment to the op - what is your reason and purpose for wanting this feature?

    I do not know Ruby much, as this was mentioned by the OP, I did some research. I see that in Ruby classes are really a special kind of object and one can create (even dynamically) new methods. Classes are full class objects in Ruby, they are not in Java. This is just something you will have to accept when working with Java (or C#). These are not dynamic languages, though C# is adding some forms of dynamic. In reality, Ruby does not have "static" methods as far as I could find - in that case these are methods on the singleton class object. You can then override this singleton with a new class and the methods in the previous class object will call those defined in the new class (correct?). So if you called a method in the context of the original class it still would only execute the original statics, but calling a method in the derived class, would call methods either from the parent or sub-class. Interesting and I can see some value in that. It takes a different thought pattern.

    Since you are working in Java, you will need to adjust to that way of doing things. Why they did this? Well, probably to improve performance at the time based on the technology and understanding that was available. Computer languages are constantly evolving. Go back far enough and there is no such thing as OOP. In the future, there will be other new ideas.

    EDIT: One other comment. Now that I see the differences and as I Java/C# developer myself, I can understand why the answers you get from Java developers may be confusing if you are coming from a language like Ruby. Java static methods are not the same as Ruby class methods. Java developers will have a hard time understanding this, as will conversely those who work mostly with a language like Ruby/Smalltalk. I can see how this would also be greatly confusing by the fact that Java also uses "class method" as another way to talk about static methods but this same term is used differently by Ruby. Java does not have Ruby style class methods (sorry); Ruby does not have Java style static methods which are really just old procedural style functions, as found in C.

    By the way - thanks for the question! I learned something new for me today about class methods (Ruby style).

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

    By overriding we can create a polymorphic nature depending on the object type. Static method has no relation with object. So java can not support static method overriding.

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

    Well... the answer is NO if you think from the perspective of how an overriden method should behave in Java. But, you don't get any compiler error if you try to override a static method. That means, if you try to override, Java doesn't stop you doing that; but you certainly don't get the same effect as you get for non-static methods. Overriding in Java simply means that the particular method would be called based on the run time type of the object and not on the compile time type of it (which is the case with overriden static methods). Okay... any guesses for the reason why do they behave strangely? Because they are class methods and hence access to them is always resolved during compile time only using the compile time type information. Accessing them using object references is just an extra liberty given by the designers of Java and we should certainly not think of stopping that practice only when they restrict it :-)

    Example: let's try to see what happens if we try overriding a static method:-

    class SuperClass {
    // ......
    public static void staticMethod() {
        System.out.println("SuperClass: inside staticMethod");
    }
    // ......
    }
    
    public class SubClass extends SuperClass {
    // ......
    // overriding the static method
    public static void staticMethod() {
        System.out.println("SubClass: inside staticMethod");
    }
    
    // ......
    public static void main(String[] args) {
        // ......
        SuperClass superClassWithSuperCons = new SuperClass();
        SuperClass superClassWithSubCons = new SubClass();
        SubClass subClassWithSubCons = new SubClass();
    
        superClassWithSuperCons.staticMethod();
        superClassWithSubCons.staticMethod();
        subClassWithSubCons.staticMethod();
        // ...
    }
    }
    

    Output:-
    SuperClass: inside staticMethod
    SuperClass: inside staticMethod
    SubClass: inside staticMethod

    Notice the second line of the output. Had the staticMethod been overriden this line should have been identical to the third line as we're invoking the 'staticMethod()' on an object of Runtime Type as 'SubClass' and not as 'SuperClass'. This confirms that the static methods are always resolved using their compile time type information only.

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

    Actually we were wrong.
    Despite Java doesn't allow you to override static methods by default, if you look thoroughly through documentation of Class and Method classes in Java, you can still find a way to emulate static methods overriding by following workaround:

    import java.lang.reflect.InvocationTargetException;
    import java.math.BigDecimal;
    
    class RegularEmployee {
    
        private BigDecimal salary = BigDecimal.ONE;
    
        public void setSalary(BigDecimal salary) {
            this.salary = salary;
        }
        public static BigDecimal getBonusMultiplier() {
            return new BigDecimal(".02");
        }
        public BigDecimal calculateBonus() {
            return salary.multiply(this.getBonusMultiplier());
        }
        public BigDecimal calculateOverridenBonus() {
            try {
                // System.out.println(this.getClass().getDeclaredMethod(
                // "getBonusMultiplier").toString());
                try {
                    return salary.multiply((BigDecimal) this.getClass()
                        .getDeclaredMethod("getBonusMultiplier").invoke(this));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            }
            return null;
        }
        // ... presumably lots of other code ...
    }
    
    final class SpecialEmployee extends RegularEmployee {
    
        public static BigDecimal getBonusMultiplier() {
            return new BigDecimal(".03");
        }
    }
    
    public class StaticTestCoolMain {
    
        static public void main(String[] args) {
            RegularEmployee Alan = new RegularEmployee();
            System.out.println(Alan.calculateBonus());
            System.out.println(Alan.calculateOverridenBonus());
            SpecialEmployee Bob = new SpecialEmployee();
            System.out.println(Bob.calculateBonus());
            System.out.println(Bob.calculateOverridenBonus());
        }
    }
    

    Resulting output:

    0.02
    0.02
    0.02
    0.03
    

    what we were trying to achieve :)

    Even if we declare third variable Carl as RegularEmployee and assign to it instance of SpecialEmployee, we will still have call of RegularEmployee method in first case and call of SpecialEmployee method in second case

    RegularEmployee Carl = new SpecialEmployee();
    
    System.out.println(Carl.calculateBonus());
    System.out.println(Carl.calculateOverridenBonus());
    

    just look at output console:

    0.02
    0.03
    

    ;)

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

    Easy solution: Use singleton instance. It will allow overrides and inheritance.

    In my system, I have SingletonsRegistry class, which returns instance for passed Class. If instance is not found, it is created.

    Haxe language class:

    package rflib.common.utils;
    import haxe.ds.ObjectMap;
    
    
    
    class SingletonsRegistry
    {
      public static var instances:Map<Class<Dynamic>, Dynamic>;
    
      static function __init__()
      {
        StaticsInitializer.addCallback(SingletonsRegistry, function()
        {
          instances = null;
        });
    
      } 
    
      public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
      {
        if (instances == null) {
          instances = untyped new ObjectMap<Dynamic, Dynamic>();      
        }
    
        if (!instances.exists(cls)) 
        {
          if (args == null) args = [];
          instances.set(cls, Type.createInstance(cls, args));
        }
    
        return instances.get(cls);
      }
    
    
      public static function validate(inst:Dynamic, cls:Class<Dynamic>)
      {
        if (instances == null) return;
    
        var inst2 = instances[cls];
        if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
      }
    
    }
    
    0 讨论(0)
提交回复
热议问题