java - overloaded - static override c++




為什麼Java不允許重寫靜態方法? (15)

為什麼不可能覆蓋靜態方法?

如果可能,請使用示例。


A Static method, variable, block or nested class belongs to the entire class rather than an object.

A Method in Java is used to expose the behaviour of an Object / Class. Here, as the method is static (ie, static method is used to represent the behaviour of a class only.) changing/ overriding the behaviour of entire class will violate the phenomenon of one of the fundamental pillar of Object oriented programming ie, high cohesion . (remember a constructor is a special kind of method in Java.)

High Cohesion - One class should have only one role. For example: A car class should produce only car objects and not bike, trucks, planes etc. But the Car class may have some features(behaviour) that belongs to itself only.

Therefore, while designing the java programming language. The language designers thought to allow developers to keep some behaviours of a class to itself only by making a method static in nature.

The below piece code tries to override the static method, but will not encounter any compilation error.

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

This is because, here we are not overriding a method but we are just re-declaring it. Java allows re-declaration of a method (static/non-static).

Removing the static keyword from getVehileNumber() method of Car class will result into compilation error, Since, we are trying to change the functionality of static method which belongs to Vehicle class only.

Also, If the getVehileNumber() is declared as final then the code will not compile, Since the final keyword restricts the programmer from re-declaring the method.

public static final int getVehileNumber() {
return VIN;     }

Overall, this is upto software designers for where to use the static methods. I personally prefer to use static methods to perform some actions without creating any instance of a class. Secondly, to hide the behaviour of a class from outside world.


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.


By overriding, you achieve dynamic polymorhpism. When you say overridng 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 polymorhpic behavior comes when 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 class name, which will be linked at compile time, so there is no concept of linking methods at run time 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.


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!";
  }

}

I like and double Jay's comment ( https://.com/a/2223803/1517187 ).
I agree that this is 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 the first language implementing OOP.
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 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.


Now seeing above answers everyone knows that we can't override static methods, but one should not misunderstood about the concept of accessing static methods from subclass .

We can access static methods of super class with subclass reference if this static method has not been hidden by new static method defined in sub class.

For Example, see below code:-

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

輸出: -

SuperClass saying Hello

See Java oracle docs and search for What You Can Do in a Subclass for details about hiding of static methods in sub class.

謝謝


The following code shows that it is possible:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

What good will it do to override static methods. You cannot call static methods through an instance.

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT : It appears that through an unfortunate oversight in language design, you can call static methods through an instance. Generally nobody does that. My bad.


一般來說,允許'重寫'靜態方法是沒有意義的,因為沒有好的方法來確定在運行時調用哪一個。 拿Employee函數來說,如果我們調用RegularEmployee.getBonusMultiplier() - 哪個方法應該被執行?

在Java的情況下,人們可以想像一種語言定義,只要通過對象實例調用靜態方法,就可以“覆蓋”靜態方法。 但是,所有這些都是為了重新實現常規的類方法,為語言增加冗餘而又不增加任何好處。


其實我們錯了。
儘管默認情況下Java不允許您重寫靜態方法,但如果仔細查看Java中的Class和Method類的文檔,仍然可以找到一種方法來仿效以下解決方法覆蓋靜態方法:

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());
    }
}

結果輸出:

0.02
0.02
0.02
0.03

我們試圖達到的目標:)

即使我們將第三個變量Carl聲明為RegularEmployee並將其分配給SpecialEmployee實例,我們仍然會在第一種情況下調用RegularEmployee方法,在第二種情況下調用SpecialEmployee方法

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

只要看看輸出控制台:

0.02
0.03

;)


我個人認為這是Java設計中的一個缺陷。 是的,是的,我了解非靜態方法附加到實例,而靜態方法附加到類等等。仍然,請考慮以下代碼:

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");
    }
}

這段代碼不會像你所期望的那樣工作。 也就是說,SpecialEmployee像普通員工一樣獲得2%的獎金。 但是如果你刪除了“靜態”,那麼SpecialEmployee就會獲得3%的獎勵。

(誠然,這個例子是糟糕的編碼風格,因為在現實生活中,你可能希望獎金乘數在某個數據庫中,而不是硬編碼。但那是因為我不想讓這個例子很多與該點無關的代碼)。

我覺得你可能想讓getBonusMultiplier是靜態的,這似乎很合理。 也許你希望能夠為所有類別的員工顯示獎金乘數,而不需要在每個類別中都有一個員工實例。 搜索這種示例實例的重點是什麼? 如果我們要創建一個新的員工類別並且沒有任何員工分配給它,該怎麼辦? 這在邏輯上是一個靜態函數。

但它不起作用。

是的,是的,我可以想出許多方法來重寫上述代碼以使其工作。 我的觀點並不是它造成了一個無法解決的問題,而是它為粗心的程序員創造了一個陷阱,因為這種語言不像我認為合理的人會期望的那樣行事。

也許如果我試圖編寫OOP語言的編譯器,我很快就會明白為什麼要實現它,以便可以重寫靜態函數將是困難的或不可能的。

或者,Java的行為方式可能有很好的理由。 任何人都可以指出這種行為的優勢,某些類別的問題通過這種方式變得更容易? 我的意思是,不要把我指向Java語言規範,並說“看,這是記錄它的行為”。 我知道。 但是,它有一個很好的理由說明它應該如此表現? (除了明顯的“讓它工作正確太難了......”)

更新

@VicKirk:如果你的意思是這是“糟糕的設計”,因為它不適合Java處理靜態的問題,我的回答是,“呃,當然。” 正如我在我原來的帖子中所說的那樣,它不起作用。 但是,如果你的意思是說這是一種糟糕的設計,從某種意義上講,這種語言在某種程度上會出現根本性的錯誤,也就是說靜態可以像虛擬功能一樣被覆蓋,這會以某種方式引入歧義,或者不可能有效地實施或者這樣做,我回答說:“為什麼?這個概念有什麼問題?”

我想我給出的例子是一件很自然的事情,想要做。 我有一個具有不依賴於任何實例數據的函數的類,並且我可能非常合理地想要獨立於實例調用,也希望從實例方法中調用。 為什麼這不適用? 多年來,我遇到過這種情況,相當多次。 在實踐中,我通過使函數變為虛擬來解決它,然後創建一個靜態方法,其唯一目的是將靜態方法傳遞給具有虛擬實例的虛擬方法。 這似乎是一個非常迂迴的方式去那裡。


方法重寫可以通過動態分派實現 ,這意味著對象的聲明類型不會確定其行為,而是其運行時類型:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

即使lassiekermit都聲明為Animal類型的對象,但它們的行為(method .speak() )會有所不同,因為動態分派只會在運行時將方法調用.speak() bind到實現 - 而不是在編譯時。

現在,這裡是static關鍵字開始有意義的地方: “static”這個詞是“dynamic”的反義詞。 所以你不能重寫靜態方法的原因是因為靜態成員沒有動態調度 - 因為靜態字面意思是“不動態”。 如果他們動態調度(因此可能被重寫), static關鍵字就沒有意義了。


那麼......如果你從一個重寫方法應該如何在Java中表現的角度思考,答案是否定的。 但是,如果您嘗試覆蓋靜態方法,則不會收到任何編譯器錯誤。 這意味著,如果您嘗試重寫,Java不會阻止您這麼做; 但是你肯定不會獲得與非靜態方法相同的效果。 在Java中重寫僅僅意味著將根據對象的運行時類型而不是它的編譯時類型(這是使用overriden靜態方法的情況)調用特定的方法。 好吧...有什麼猜測他們為什麼會表現得很奇怪? 因為它們是類方法,因此只有在編譯時才使用編譯時類型信息來解析對它們的訪問。 使用對象引用來訪問它們只是Java設計者給出的額外自由,我們當然不應該想到只有在限制它時才停止這種做法:-)

示例 :讓我們試著看看如果我們重寫一個靜態方法會發生什麼: -

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();
    // ...
}
}

輸出 : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

注意輸出的第二行。 如果staticMethod被覆蓋,這行應該與第三行相同,因為我們在運行時類型的對像上調用'staticMethod()'作為'SubClass',而不是'SuperClass'。 這證實了靜態方法總是使用它們的編譯時類型信息來解析。


重寫取決於有一個類的實例。 多態性的要點是,您可以繼承一個類的子類,並且實現這些子類的對像對於超類中定義的相同方法(並在子類中重寫)將具有不同的行為。 靜態方法與類的任何實例都沒有關聯,所以這個概念是不適用的。

推動Java設計的兩個考慮因素影響了這一點。 其中一個問題是性能問題:Smalltalk對它的速度太慢了(垃圾收集和多態調用是其中的一部分),Java的創建者決心避免這種情況。 另一個決定是Java的目標受眾是C ++開發人員。 使得靜態方法的工作方式具有熟悉C ++程序員的好處,而且速度也非常快,因為不需要等到運行時才能確定要調用的方法。


靜態方法被JVM視為全局對象,根本沒有綁定到對象實例。

從概念上說,如果你可以從類對像中調用靜態方法(比如像Smalltalk這樣的語言),但在Java中不是這樣。

編輯

你可以重載靜態方法,沒關係。 但是你不能重寫一個靜態方法,因為類不是一流的對象。 您可以在運行時使用反射來獲取對象的類,但獲取的對像不會與類層次結構並行。

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

你可以反思這些課程,但它會停留在那裡。 您不通過使用clazz1.staticMethod() ,而是使用MyClass.staticMethod()來調用靜態方法。 一個靜態方法不會綁定到一個對象,因此在靜態方法中不存在thissuper概念。 靜態方法是一個全局函數; 因此也沒有多態性的概念,因此,方法重寫是沒有意義的。

但是如果MyClass是運行時的一個對象,就像在Smalltalk中一樣(或者JRuby作為一個評論表明,但我對JRuby一無所知),這可能是可能的。

噢,還有一件事。 你可以通過一個對象obj1.staticMethod()調用一個靜態方法,但是MyClass.staticMethod()是真正的語法糖,應該避免。 它通常會在現代IDE中引發警告。 我不知道他們為什麼允許這條捷徑。





static-methods