Zachowanie końcowej metody statycznej

124

Bawiłem się modyfikatorami metodą statyczną i zauważyłem dziwne zachowanie.

Jak wiemy, metod statycznych nie można przesłonić, ponieważ są one skojarzone z klasą, a nie instancją.

Więc jeśli mam poniższy fragment, kompiluje się dobrze

//Snippet 1 - Compiles fine
public class A {
    static void ts() {
    }
}

class B extends A {
    static void ts() {
    }
}

Ale jeśli dołączę ostateczny modyfikator do metody statycznej w klasie A, kompilacja nie powiedzie się ts () w B nie może przesłonić ts () w A; nadpisana metoda jest statyczna ostateczna .

Dlaczego tak się dzieje, gdy metody statycznej nie można w ogóle zastąpić?

Harish
źródło
2
wydaje się dziwne, +1 za pytanie, ale do tej pory żadna z odpowiedzi nie jest zadowalająca.
Rakesh Juyal
1
To nie jest przesłonięte. Nadal jest w A.ts ().
Alex Feinman

Odpowiedzi:

166

Metody statyczne nie mogą zostać zastąpione, ale można je ukryć. ts()Metoda B nie jest nadrzędne (nie podlega polimorfizmu) ts()od A, ale będzie to ukryć. Jeśli zawołasz ts()B (NIE A.ts()lub B.ts()... po prostu ts()), zostanie wywołany ten z B, a nie A. Ponieważ nie jest to poddane polimorfizmowi, wywołaniets() w A nigdy nie zostanie przekierowane do tego w B.

Słowo kluczowe finalwyłącza ukrywanie metody. Dlatego nie można ich ukryć, a próba zrobienia tego spowoduje błąd kompilatora.

Mam nadzieję że to pomoże.

NawaMan
źródło
30
Aby być może dokończenie odpowiedzi, co wydaje mi się słuszne, problem polega w zasadzie na złym komunikacie o błędzie kompilatora: powinien on mówić, że B nie może ukryć ts () w A. Deklarowanie metody statycznej final oznacza deklarowanie, że nie można jej ukryć.
Sean Owen
2
@Sean Owen: Ja też tak myślę. Termin „ukryj” jest nawet używany w specyfikacji Java, więc dlaczego nie użyć go w komunikacie kompilatora.
NawaMan
2
Dlaczego jest to w ogóle funkcja? W jakim kontekście byłoby to przydatne?
user253751
public class Test {końcowy statyczny public void main (String ... srik) {System.out.println ("W metodzie głównej"); ts (); } public static void ts () {Child c = new Child (); c.ts (); System.out.println ("Test ts"); }} public class Child extends Test {public static void ts () {System.out.println ("Child ts"); }} Cześć, czy u plz wyjaśnij mi, co dzieje się w tym scenariuszu
srikanth r
@SeanOwen Myślę, że to też nie jest poprawne, kompilator powinien powiedzieć, że skoro A#tsjest dziedziczona, a taka metoda już istnieje B, po prostu posiadanie dwóch metod z tą samą sygnaturą i innym modyfikatorem ( final) nie działałoby jako przeciążenie. Żałuję jednak, że nie
Eugene,
13

metody statyczne nie mogą zostać zastąpione

To nie jest do końca prawda. Przykładowy kod tak naprawdę oznacza, że ​​metoda ts w B ukrywa metodę ts w A. Więc nie jest to do końca przesłanianie. Na Javaranch jest ładne wyjaśnienie.

Vincent Ramdhanie
źródło
4
To prawda, ale nie jest precyzyjne. metod statycznych nie można przesłonić, ale można je ukryć, jeśli wywołujesz je w odwołaniu do instancji, a nie na nazwie klasy.
John Mercier
1
Niestety twój link już nie działa. Czy można to naprawić?
Mathias Bader
Link javaranch nie działa, ale wyszukanie słów kluczowych w
Google znalazło
Zmieniłem post, zastępując martwy link linkiem opublikowanym przez Sundeep.
MC Emperor
10

Metody statyczne należą do klasy, a nie do instancji.

A.ts()i B.ts()zawsze będą to oddzielne metody.

Prawdziwym problemem jest to, że Java umożliwia wywoływanie metod statycznych na obiekcie instancji. Metody statyczne z tym samym podpisem z klasy nadrzędnej są ukrywane, gdy są wywoływane z instancji podklasy. Nie możesz jednak przesłonić / ukryć ostatecznych metod .

Można by pomyśleć, że komunikat o błędzie użyje słowa ukrytego zamiast zastąpionego ...

Władca
źródło
6

Być może będziesz w stanie pomyśleć o ostatecznym zakończeniu metody statycznej, biorąc pod uwagę następujące kwestie:

Posiadanie następujących klas:

class A {
    static void ts() {
        System.out.print("A");
    }
}
class B extends A {
    static void ts() {
        System.out.print("B");
    }
}

Teraz „poprawnym” sposobem na wywołanie tych metod byłoby

A.ts();
B.ts();

co spowoduje, ABale możesz również wywołać metody w instancjach:

A a = new A();
a.ts();
B b = new B();
b.ts();

co by też skutkowało AB.

Rozważmy teraz następujące kwestie:

A a = new B();
a.ts();

to by się wydrukowało A. To może cię zaskoczyć, ponieważ tak naprawdę masz przedmiot klasy B. Ale ponieważ wywołujesz go z referencji typu A, zadzwoni A.ts(). Możesz wydrukować Bza pomocą następującego kodu:

A a = new B();
((B)a).ts();

W obu przypadkach posiadany obiekt pochodzi z klasy B. Ale w zależności od wskaźnika, który wskazuje na obiekt, wywołasz metodę from Alub from B.

Teraz powiedzmy, że jesteś twórcą klasy Ai chcesz zezwolić na tworzenie podklas. Ale naprawdę chcesz ts(), aby metoda , kiedykolwiek wywołana, nawet z podklasy, robiła to, co chcesz, a nie była ukryta przez wersję podklasy. Wtedy możesz to zrobić finali zapobiec ukryciu go w podklasie. I możesz być pewien, że poniższy kod wywoła metodę z Twojej klasy A:

B b = new B();
b.ts();

Ok, wprawdzie jest to w jakiś sposób skonstruowane, ale w niektórych przypadkach może to mieć sens.

Nie powinieneś wywoływać metod statycznych na instancjach, ale bezpośrednio na klasach - wtedy nie będziesz miał tego problemu. Na przykład IntelliJ IDEA wyświetli ostrzeżenie, jeśli wywołasz metodę statyczną na instancji, a także, jeśli sprawisz, że metoda statyczna zostanie ostateczna.

Mathias Bader
źródło
0

Metoda ts () w B nie przesłania metody ts () w A, jest po prostu inną metodą. Klasa B nie widzi metody ts () w A, ponieważ jest statyczna, dlatego może zadeklarować własną metodę o nazwie ts ().

Jeśli jednak metoda jest ostateczna, kompilator wykryje, że istnieje metoda ts () w A, której nie należy przesłonić w B.

amischiefr
źródło
Nie sądzę, żeby to wyjaśniało, dlaczego „ostateczne” nagle oznacza, że ​​te metody nie mogą współistnieć. Jak mówisz, bez „finału” nie ma problemu. Mówisz, że to nie jest nadrzędne, ale potem mówisz, że problem polega na tym, że B nie może zastąpić metody A.
Sean Owen
Jasne, że tak, stwierdzam, że B nie widzi metody ts () w A (jest „ukryta”), ale końcowy modyfikator nie „ukrywa” metod przed klasami rozszerzającymi inną. Ale eh, ok.
amischiefr
0

Myślę, że błąd kompilacji był tutaj dość mylący. Nie powinno było powiedzieć „zastąpiona metoda jest statyczną ostateczną metodą”, ale zamiast tego powinna powiedzieć „zastąpiona metoda jest ostateczna”. Modyfikator statyczny nie ma tutaj znaczenia.

BalusC
źródło
1
Rozważasz więc, że metoda statyczna w B zastępuje metodę w A?
Koray Tugay
@KorayTugay Zastanawiam się tylko, czy kompilator najpierw spojrzy na potencjalnie możliwą do zastąpienia metodę (przez chwilę ignoruje statyczne), widzi ostateczną wersję przy niepowodzeniu. To tylko szalone przypuszczenie
Eugene
Balus Myślę, że to jedyna niskiej jakości odpowiedź, którą masz w StackOverflow. Biorąc pod uwagę wszystkie Twoje wyjątkowe odpowiedzi, ta nie należy do nich. @BalusC
Koray Tugay
@KorayTugay: w tamtym czasie nie miałem wystarczającej reputacji, aby komentować :) Jeśli oddzwonisz, usunę odpowiedź, nie ma problemu.
BalusC
0

Metody statycznej nie można przesłonić w Javie, w przeciwieństwie do metod niestatycznych. Ale są dziedziczone jak statyczne i niestatyczne elementy członkowskie danych. Dlatego w klasie nadrzędnej nie można utworzyć metody niestatycznej o tej samej nazwie

class Writer { 
    public static void doo(){
        System.out.println("sth");
    } 
}
class Author extends Writer{ 
    public void doo(){
        System.out.println("ok"); // error overridden method is static
    }
}

Te final, zapewnia słów kluczowych, które organ specjalny sposób być prowadzone za każdym wywołanie metody. Teraz, jeśli w klasie potomnej zostanie utworzona metoda statyczna o tej samej nazwie i zostanie wykonane wywołanie metody, metoda w podklasie zostanie wykonana, co nie powinno mieć miejsca, jeśli final jest poprzedzony nazwą metody statycznej w klasie nadrzędnej . Stąd słowo kluczowe final ogranicza tworzenie metody o tej samej nazwie w klasie potomnej.

Pratik Gupta
źródło