Dlaczego metody statyczne nie mogą być abstrakcyjne w Javie?


592

Pytanie w Javie, dlaczego nie mogę zdefiniować abstrakcyjnej metody statycznej? na przykład

abstract class foo {
    abstract void bar( ); // <-- this is ok
    abstract static void bar2(); //<-- this isn't why?
}

9
Kilka powodów: metoda statyczna musi mieć ciało, nawet jeśli należą one do klasy abstrakcyjnej, ponieważ nie trzeba tworzyć instancji klasy, aby uzyskać dostęp do jej metody statycznej. Innym sposobem myślenia o tym jest to, że jeśli przez chwilę założymy, że jest to dozwolone, problem polega na tym, że wywołania metod statycznych nie dostarczają żadnych informacji o typie wykonania (RTTI), pamiętaj, że nie jest wymagane tworzenie instancji, więc nie można przekierować do ich konkretnych nadpisanych implementacji, a zatem zezwolenie na statyczne abstarct nie ma żadnego sensu. Innymi słowy, nie może zapewnić żadnej korzyści polimorfizmu, dlatego nie jest dozwolone.
sactiw

6
Jeśli masz coś do odpowiedzi na pytanie, opublikuj je jako odpowiedź , a nie komentarz
Solomon Ucko

Odpowiedzi:


569

Ponieważ „streszczenie” oznacza: „Nie implementuje żadnej funkcji”, a „statyczny” oznacza: „Istnieje funkcjonalność, nawet jeśli nie masz instancji obiektu”. I to jest logiczna sprzeczność.


353
Bardziej zwięzłą odpowiedzią byłoby „projektowanie w złym języku”. Statyczne powinno oznaczać „należy do klasy”, ponieważ tak intuicyjnie używa się, jak pokazuje to samo pytanie. Zobacz „classmethod” w Pythonie.
Alexander Ljungberg,

12
@Tomalak przepraszam, nie byłem jasny. Oczywiście metoda statyczna „należy do klasy”. Jednak tylko w tym sensie, że żyje w tej samej przestrzeni nazw. Metoda statyczna nie jest metodą samego obiektu klasy: nie działa z „tym” jako obiektem klasy i nie uczestniczy poprawnie w łańcuchu dziedziczenia. Gdyby naprawdę była to metoda klasowa, abstract staticmiałaby doskonały sens. Byłaby to metoda samego obiektu klasy, którą obiekty podklasy muszą zaimplementować. Oczywiście sposób, w jaki stoją twoje odpowiedzi, jest prawidłowy, mimo że mam obsesję na punkcie języka.
Alexander Ljungberg,

693
To nie jest logiczna sprzeczność, to wada językowa, wiele innych języków obsługuje to pojęcie. „abstrakcyjny” oznacza „zaimplementowany w podklasach”, „statyczny” oznacza „wykonany w klasie, a nie w instancjach klasy” Nie ma logicznej sprzeczności.
Eric Grange,

10
@Eric: A jednak to, co mówisz, nie ma zastosowania do abstract static: Funkcja X, która jest „zaimplementowana w podklasie”, nie może być jednocześnie „wykonana na klasie” - tylko na podklasie . Gdzie to nie jest już abstrakcyjne.
Tomalak

72
@Tomakak: Twoja logika jest okrągła. staticnie oznacza „niepusty” - to tylko konsekwencja tego, że Java nie pozwala abstrakcyjnym metodom statycznym. Oznacza to „możliwe do wywołania w klasie”. (Powinno to oznaczać „wywołanie tylko w klasie”, ale to już inna kwestia.) Jeśli abstract staticmetody obsługiwane przez Javę oczekiwałbym, że będzie to oznaczać, że metoda 1) musi być zaimplementowana przez podklasy, a 2) jest metodą klasy podklasy. Niektóre metody po prostu nie mają sensu jako metody instancji. Niestety Java nie pozwala na określenie tego podczas tworzenia abstrakcyjnej klasy bazowej (lub interfejsu).
Michael Carman

326

Słaby wygląd języka. O wiele bardziej efektywne byłoby bezpośrednie wywoływanie statycznej metody abstrakcyjnej niż tworzenie instancji wyłącznie w celu użycia tej metody abstrakcyjnej. Jest to szczególnie prawdziwe, gdy używa się klasy abstrakcyjnej jako obejścia niemożności rozszerzenia enum, co jest kolejnym złym przykładem projektu. Mam nadzieję, że rozwiążą te ograniczenia w następnym wydaniu.


25
Java jest pełna dziwnych ograniczeń. Zamknięcia, które uzyskują dostęp tylko do zmiennych końcowych, to kolejne. Pozostała lista jest prawie nieskończona. Programista Java musi znać je i ich obejścia. Aby się dobrze bawić, w wolnym czasie musimy używać czegoś lepszego niż Java. Ale nie zawracałbym sobie głowy. To ziarno do osiągnięcia postępu.
ceving,

22
Uważam, że to, co nazywacie „złym projektowaniem języka”, jest w rzeczywistości bardziej „ochronnym projektem języka”, którego celem jest ograniczenie podstawowych naruszeń zasad, które popełniają programiści z powodu niepotrzebnych funkcji językowych.
ethanfar

22
Czy koncepcja „abstrakcyjnej statyczności” narusza zasady OO?
Trevor

7
@threed, Wcale nie, ale oczywiście są ludzie, którzy twierdzą, że sama koncepcja staticsamego siebie jest już naruszeniem ....
Pacerier

4
Potrzeba statyczności jest wyraźnym dowodem, że „zasady OO” nie są tak wszechogarniające, jak zwykle się twierdzi.
Ben

147

Nie można zastąpić metody statycznej, więc uczynienie jej abstrakcyjną byłoby bez znaczenia. Co więcej, metoda statyczna w klasie abstrakcyjnej należałaby do tej klasy, a nie do klasy nadrzędnej, więc i tak nie można jej użyć.


18
Tak, szkoda, że ​​w Javie nie można zastąpić metod statycznych.
Michel

12
@Michel: jaki byłby sens? Jeśli chcesz zachować zachowanie oparte na instancji, użyj metod instancji.
Ran Biron,

8
Ta odpowiedź jest niepoprawna. Metody statyczne w klasach abstrakcyjnych działają dobrze i są powszechnie stosowane. Po prostu statyczne metody samej klasy mogą nie być abstrakcyjne. @Michel nie ma sensu zastępować metody statycznej. Bez instancji, w jaki sposób środowisko wykonawcze wiedziałoby, którą metodę wywołać?
erickson,

63
@erickson - Nawet bez wystąpienia hierarchia klas jest nienaruszona - dziedziczenie metod statycznych może działać podobnie jak dziedziczenie metod instancji. Smalltalk to robi i jest całkiem użyteczny.
Jared,

8
@matiasg to nic nowego. Klasy abstrakcyjne zawsze miały statyczne, nieabstrakcyjne metody.
Matt Ball,

70

abstractAdnotacji do metody wskazuje, że metoda musi być nadpisane w podklasie.

W Javie staticelementu (metody lub pola) nie można zastąpić podklasami (niekoniecznie jest to prawda w innych językach obiektowych, patrz SmallTalk.) Element staticczłonkowski może być ukryty , ale zasadniczo różni się od przesłaniania .

Ponieważ elementów statycznych nie można zastąpić w podklasie, abstractadnotacja nie może zostać do nich zastosowana.

Nawiasem mówiąc - inne języki obsługują dziedziczenie statyczne, podobnie jak dziedziczenie instancji. Z perspektywy składni te języki zwykle wymagają włączenia nazwy klasy do instrukcji. Na przykład w Javie, przy założeniu, że piszesz kod w klasie A, są to równoważne instrukcje (jeśli metoda A () jest metodą statyczną i nie ma metody instancji o tej samej sygnaturze):

ClassA.methodA();

i

methodA();

W SmallTalk nazwa klasy nie jest opcjonalna, więc składnia jest następująca (zwróć uwagę, że SmallTalk nie używa. Do oddzielenia „podmiotu” i „czasownika”, ale zamiast tego używa go jako terminatora statemend):

ClassA methodA.

Ponieważ nazwa klasy jest zawsze wymagana, poprawną „wersję” metody można zawsze ustalić, przechodząc przez hierarchię klas. Za to, co warto, czasami tęsknięstatic dziedziczenia i ugryziony przez brak statycznego dziedziczenia w Javie, kiedy zaczynałem od niego. Dodatkowo SmallTalk jest typu kaczego (i dlatego nie obsługuje programu według umowy). Zatem nie ma abstractmodyfikatora dla członków klasy.


1
„Element statyczny nie może zostać zastąpiony przez podklasy” jest nieprawidłowy. Jest to możliwe, przynajmniej w Javie6. Nie jestem pewien, kiedy tak jest.
Steven De Groote,

15
@Steven De Groote Człon statyczny nie może zostać zastąpiony przez podklasy. Jeśli podklasa ma metodę statyczną z taką samą sygnaturą jak metoda statyczna w nadklasie, nie zastępuje jej, ukrywa. http://docs.oracle.com/javase/tutorial/java/IandI/override.html Różnica polega na tym, że polimorfizm działa tylko w przypadku przesłoniętych, ale nie ukrytych metod.
John29,

2
@ John29 Dzięki za wyjaśnienie, ale oprócz różnicy w nazwach, wydaje się podobny w użyciu.
Steven De Groote,

2
@Steven De Groote Tak, jest podobny w użyciu, ale zachowanie jest inne. To jest powód, dla którego nie ma statycznych metod abstrakcyjnych - jaki jest sens statycznych metod abstrakcyjnych, jeśli nie obsługują polimorfizmu?
John29,

3
@Steven De Groote: Różnica staje się widoczna, gdy wywołuje się tę metodę w samej superklasie. Załóżmy, że Super.foo wywołuje Super.bar. Jeśli podklasa implementuje Subclass.bar, a następnie wywołuje foo, wówczas foo nadal wywoła Super.bar, a nie Subclass.bar. Stąd to, co naprawdę masz, to dwie zupełnie różne i niepowiązane ze sobą metody, obie nazywane „bar”. Nie jest to nadrzędne w żadnym przydatnym znaczeniu.
Doradus

14

Zadałem również to samo pytanie, oto dlaczego

Ponieważ klasa Abstract mówi, nie da implementacji i nie pozwoli na jej podklasę

więc podklasa musi zastąpić metody nadklasy,

ZASADA 1 - Nie można zastąpić metody statycznej

Ponieważ elementy i metody statyczne są elementami czasu kompilacji, dlatego przeciążanie (polimorfizm czasu kompilacji) metod statycznych jest dozwolone zamiast zastępowania (polimorfizm środowiska uruchomieniowego)

Więc nie mogą być abstrakcyjni.

Nie ma czegoś takiego jak abstrakcyjny statyczny <--- Niedozwolone w Java Universe


5
-1, nie jest prawdą, że „Java nie pozwala na przesłonięcie metody statycznej, ponieważ elementy i metody statyczne są elementami czasu kompilacji”. Statyczne typu kontrola jest zdecydowanie możliwe abstract staticzobaczyć stackoverflow.com/questions/370962/... . Prawdziwy powód dlaczego Java nie pozwala metody statyczne być zastąpiona jest ponieważ Java nie pozwala metody statyczne być nadpisane.
Pacerier

Przeładowanie nie ma nic wspólnego z polimorfizmem. Przeładowanie i przesłonięcie nie mają ze sobą nic wspólnego oprócz przedrostka „over” w bardzo podobny sposób, w jaki Java i JavaScript mają w sobie zarówno „Java”. Nazwa metody nie jest tym, co ją identyfikuje, lecz jej podpis. więc foo(String)to nie to samo, co foo(Integer)- to wszystko.
Captain Man,

@CaptainMan Przeciążenie jest dosłownie nazywane „polimorfizmem parametrycznym”, ponieważ w zależności od typu parametru wywoływana jest inna metoda, która jest polimorfizmem.
Davor

12

To okropny projekt językowy i naprawdę nie ma powodu, dla którego to niemożliwe.

W rzeczywistości oto implementacja tego, jak MOŻNA to zrobić w JAVA :

public class Main {

        public static void main(String[] args) {
                // This is done once in your application, usually at startup
                Request.setRequest(new RequestImplementationOther());

                Request.doSomething();
        }

        public static final class RequestImplementationDefault extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something AAAAAA");
                }
        }

        public static final class RequestImplementaionOther extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something BBBBBB");
                }
        }

        // Static methods in here can be overriden
        public static abstract class Request {

                abstract void doSomethingImpl();

                // Static method
                public static void doSomething() {
                        getRequest().doSomethingImpl();
                }

                private static Request request;
                private static Request getRequest() {
                        // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. 
                        if ( request == null ) {
                                return request = new RequestImplementationDefault();
                        }
                        return request;
                }
                public static Request setRequest(Request r){
                        return request = r;
                }

        }
}

================= Stary przykład poniżej =================

Poszukaj getRequest i getRequestImpl ... setInstance można wywołać w celu zmiany implementacji przed wykonaniem wywołania.

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author Mo. Joseph
 * @date 16 mar 2012
 **/

public abstract class Core {


    // ---------------------------------------------------------------        
    private static Core singleton; 
    private static Core getInstance() {
        if ( singleton == null )
            setInstance( new Core.CoreDefaultImpl() );  // See bottom for CoreDefaultImpl

        return singleton;
    }    

    public static void setInstance(Core core) {
        Core.singleton = core;
    }
    // ---------------------------------------------------------------        



    // Static public method
    public static HttpServletRequest getRequest() {      
        return getInstance().getRequestImpl();
    }


    // A new implementation would override this one and call setInstance above with that implementation instance
    protected abstract HttpServletRequest getRequestImpl();




    // ============================ CLASSES =================================

    // ======================================================================
    // == Two example implementations, to alter getRequest() call behaviour 
    // == getInstance() have to be called in all static methods for this to work
    // == static method getRequest is altered through implementation of getRequestImpl
    // ======================================================================

    /** Static inner class CoreDefaultImpl */
    public static class CoreDefaultImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

     /** Static inner class CoreTestImpl : Alternative implementation */
    public static class CoreTestImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return new MockedRequest();
        }
    }       

}

Używane w następujący sposób:

static {
     Core.setSingleton(new Core.CoreDefaultImpl());

     // Or

     Core.setSingleton(new Core.CoreTestImpl());

     // Later in the application you might use

     Core.getRequest(); 

}

6
Nie zrozumiałem, gdzie podałeś przykład abstract staticmetody zadanej w pytaniu i napisałeś pogrubioną czcionką MOŻNA ZROBIĆ W JAVA . To całkowicie mylące.
Blip

1
Samo w sobie nie pozwala zdefiniować go jako abstrakcyjnego statycznego, ale można osiągnąć podobny wynik, w którym można zmienić implementację metody statycznej za pomocą tego wzorca / hacka. Trudno się pomylić. Można to zrobić, ale używając innej semantyki.
mmm

Jest to przykład rozszerzenia klasy abstrakcyjnej, a następnie umieszczenia metod statycznych w dziecku. To nie jest przykład MOŻNA ZROBIĆ W JAVA, w jakikolwiek sposób.
Scuba Steve

2
@ScubaSteve Po pierwsze, mylisz się co do swoich wniosków. Po drugie, osiąga ten sam wynik. Oznacza to, że statyczny dostęp do klasy można zmienić za pomocą innej implementacji. To nie jest odpowiedź na powiedzenie, że uczyniłem słowo kluczowe statyczne abstrakcyjnym, ale używając tego wzorca możesz użyć metod statycznych i nadal zmieniać ich implementację. Ma to jednak negatywny wpływ na to, że jest globalne, ale w środowisku test / prod / dev robi to dla nas.
mmm

5
  • Metodę abstrakcyjną definiuje się tylko po to, aby można ją było zastąpić w podklasie. Jednak metod statycznych nie można zastąpić. Dlatego posiadanie abstrakcyjnej, statycznej metody jest błędem podczas kompilacji.

    Teraz następne pytanie brzmi: dlaczego nie można zastąpić metod statycznych?

  • Jest tak, ponieważ metody statyczne należą do konkretnej klasy, a nie do jej instancji. Jeśli spróbujesz zastąpić metodę statyczną, nie pojawi się żaden błąd kompilacji ani środowiska wykonawczego, ale kompilator po prostu ukryje metodę statyczną nadklasy.


4

Metoda statyczna z definicji nie musi wiedzieć this. Zatem nie może to być metoda wirtualna (która jest przeciążona zgodnie z informacjami dynamicznej podklasy dostępnymi za pośrednictwem this); zamiast tego przeciążenie metody statycznej opiera się wyłącznie na informacjach dostępnych w czasie kompilacji (oznacza to: po odwołaniu się do statycznej metody nadklasy wywołuje się metodę nadklasy, ale nigdy metodę podklasy).

Zgodnie z tym abstrakcyjne metody statyczne byłyby zupełnie bezużyteczne, ponieważ nigdy nie zastąpi się odniesienia przez jakieś określone ciało.


4

Widzę, że istnieją już bogie odpowiedzi, ale nie widzę żadnych praktycznych rozwiązań. Oczywiście jest to prawdziwy problem i nie ma dobrego powodu, aby wykluczyć tę składnię w Javie. Ponieważ w pierwotnym pytaniu brakuje kontekstu, w którym może to być potrzebne, przedstawiam zarówno kontekst, jak i rozwiązanie:

Załóżmy, że masz metodę statyczną w grupie identycznych klas. Te metody nazywają metodę statyczną specyficzną dla klasy:

class C1 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C1
    }
}
class C2 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C2
    }
}

doWork()metody C1i C2są identyczne. Może istnieć wiele takich przypadków: C3 C4itd. Gdyby static abstractbyło to dozwolone, wyeliminowałbyś duplikat kodu, wykonując coś takiego:

abstract class C {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }

    static abstract void doMoreWork(int k);
}

class C1 extends C {
    private static void doMoreWork(int k) {
        // code for class C1
    }
}

class C2 extends C {
    private static void doMoreWork(int k) {
        // code for class C2
    }
}

ale nie skompiluje się, ponieważ static abstractkombinacja nie jest dozwolona. Można to jednak obejść za pomocą static classkonstrukcji, która jest dozwolona:

abstract class C {
    void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    abstract void doMoreWork(int k);
}
class C1 {
    private static final C c = new  C(){  
        @Override void doMoreWork(int k) {
            System.out.println("code for C1");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}
class C2 {
    private static final C c = new C() {
        @Override void doMoreWork(int k) {
            System.out.println("code for C2");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}

Dzięki temu rozwiązaniu jedynym zduplikowanym kodem jest

    public static void doWork() {
        c.doWork();
    }

1
Ponieważ w ostatecznym rozwiązaniu klasa abstrakcyjna C nie ma żadnych metod statycznych, dlaczego nie po prostu pozwolić, by C1 i C2 ją rozszerzyły i zastąpiły metodę doMoreWork (), a inne klasy wykonały instancję i wywołały wymagane metody. Zasadniczo robisz to samo, tj. Rozszerzając klasę C za pomocą anonimowej klasy, a następnie w C1 i C2 za pomocą jej instancji statycznej, aby umożliwić dostęp z poziomu metody statycznej, ale nie jest to wcale wymagane.
sactiw

Nie rozumiem kontekstu, który tutaj podałeś. W ostatecznym rozwiązaniu możesz zadzwonić C1.doWork()lub C2.doWork()Ale nie możesz zadzwonić C.doWork(). Również w podanym przez ciebie przykładzie, który nie będzie działał, załóżmy, że gdyby było dozwolone, to w jaki sposób klasa Cznajdzie implementację doMoreWork()? Na koniec nazwałbym twój kod kontekstowy złym projektem. Dlaczego? po prostu dlatego, że utworzono osobną funkcję dla kodu, która jest unikalna, zamiast tworzyć funkcję dla kodu, który jest wspólny, a następnie implementować funkcję statyczną w klasie C. To jest łatwiejsze !!!
Blip

2

Załóżmy, że istnieją dwie klasy Parenti Child. Parentjest abstract. Deklaracje są następujące:

abstract class Parent {
    abstract void run();
}

class Child extends Parent {
    void run() {}
}

Oznacza to, że każde wystąpienie Parentmusi określać sposób run()wykonania.

Załóżmy jednak, że Parenttak nie jest abstract.

class Parent {
    static void run() {}
}

Oznacza to, że Parent.run()wykona metodę statyczną.

Definicja abstractmetody to „Metoda zadeklarowana, ale nie zaimplementowana”, co oznacza, że ​​sama niczego nie zwraca.

Definicja staticmetody to „Metoda, która zwraca tę samą wartość dla tych samych parametrów bez względu na instancję, w której jest wywoływana”.

An abstractwartość zwracana METHOD może ulec zmianie wraz ze zmianami instancji. staticMetoda nie będzie. static abstractMetoda jest dość dużo metoda, w której wartość zwracana jest stała, ale niczego nie powrócić. To logiczna sprzeczność.

Poza tym static abstractmetoda nie ma zbyt wiele powodów .


2

Klasa abstrakcyjna nie może mieć metody statycznej, ponieważ abstrakcja jest wykonywana w celu osiągnięcia WIĄZANIA DYNAMICZNEGO, podczas gdy metody statyczne są statycznie powiązane z ich funkcjonalnością. Metoda statyczna oznacza zachowanie niezależne od zmiennej instancji, więc nie jest wymagana żadna instancja / obiekt. Tylko klasa. Metody statyczne należą do klasy, a nie do obiektu. Są one przechowywane w obszarze pamięci znanym jako PERMGEN, skąd jest współdzielony z każdym obiektem. Metody w klasie abstrakcyjnej są dynamicznie powiązane z ich funkcjonalnością.


1
Klasy abstrakcyjne z pewnością mogą mieć metody statyczne. Ale nie statyczne abstrakcyjne metody.
JacksOnF1re

2

Deklaracja metody jako staticoznacza, że ​​możemy wywołać tę metodę po nazwie klasy, a jeśli ta klasa abstractrównież jest, nie ma sensu nazywać go, ponieważ nie zawiera on żadnego ciała, a zatem nie możemy zadeklarować metody zarówno jako, jak statici abstract.


2

Ponieważ metody abstrakcyjne należą do klasy i nie mogą zostać zastąpione przez klasę implementującą, nawet jeśli istnieje metoda statyczna z tym samym podpisem, ukrywa metodę, nie zastępując jej. Nie ma zatem znaczenia, aby deklarować metodę abstrakcyjną jako statyczną, ponieważ nigdy nie otrzyma ona ciała, dlatego też błąd czasu kompilacji.


1

Metodę statyczną można wywołać bez wystąpienia klasy. W twoim przykładzie możesz wywołać foo.bar2 (), ale nie foo.bar (), ponieważ dla bar potrzebujesz instancji. Działa następujący kod:

foo var = new ImplementsFoo();
var.bar();

Wywołanie metody statycznej spowoduje wykonanie zawsze tego samego kodu. W powyższym przykładzie, nawet jeśli przedefiniujesz bar2 w ImplementsFoo, wywołanie var.bar2 () wykona foo.bar2 ().

Jeśli bar2 nie ma teraz implementacji (to znaczy streszczenie), możesz wywołać metodę bez implementacji. To bardzo szkodliwe.


1
I abstrakcyjna metoda statyczna mogłaby zostać wywołana bez instancji, ale wymagałoby to implementacji w klasie potomnej. Nie jest to dokładnie polimorfizm, ale jedynym sposobem na obejście tego jest posiadanie przez dziecko konkretnego interfejsu, który „wymaga” „abstrakcyjnej metody statycznej”. Bałagan, ale wykonalny.
fijiaaron,

3
właściwie się myliłem. W interfejsie nie można również mieć metod statycznych. Wada językowa.
fijiaaron,

1

Wydaje mi się, że znalazłem odpowiedź na to pytanie w postaci tego, dlaczego metody interfejsu (które działają jak metody abstrakcyjne w klasie nadrzędnej) nie mogą być statyczne. Oto pełna odpowiedź (nie moja)

Zasadniczo metody statyczne można wiązać w czasie kompilacji, ponieważ aby je wywołać, należy określić klasę. Różni się to od metod instancji, dla których klasa odwołania, z którego wywołujesz metodę, może być nieznana w czasie kompilacji (dlatego który blok kodu jest wywoływany, można określić tylko w czasie wykonywania).

Jeśli wywołujesz metodę statyczną, znasz już klasę, w której jest ona zaimplementowana, lub wszelkie jej bezpośrednie podklasy. Jeśli zdefiniujesz

abstract class Foo {
    abstract static void bar();
}

class Foo2 {
    @Override
    static void bar() {}
}

Wtedy każde Foo.bar();połączenie jest oczywiście nielegalne i zawsze będziesz używać Foo2.bar();.

Mając to na uwadze, jedynym celem statycznej abstrakcyjnej metody byłoby wymuszenie podklas w celu wdrożenia takiej metody. Na początku możesz pomyśleć, że jest to BARDZO złe, ale jeśli masz parametr typu ogólnego, <E extends MySuperClass>dobrze byłoby zagwarantować za pośrednictwem interfejsu, który Emoże .doSomething(). Należy pamiętać, że ze względu na wymazywanie typów, generycy istnieją tylko w czasie kompilacji.

Czy byłoby to przydatne? Tak, a może dlatego Java 8 zezwala na metody statyczne w interfejsach (choć tylko z domyślną implementacją). Dlaczego nie abstrakcyjne metody statyczne z domyślną implementacją w klasach? Po prostu dlatego, że abstrakcyjna metoda z domyślną implementacją jest w rzeczywistości konkretną metodą.

Dlaczego nie abstrakcyjne / statyczne metody interfejsu bez domyślnej implementacji? Najwyraźniej tylko z powodu sposobu, w jaki Java identyfikuje blok kodu, który musi wykonać (pierwsza część mojej odpowiedzi).


1

Ponieważ klasa abstrakcyjna jest koncepcją OOPS, a elementy statyczne nie są częścią OOPS ...
Teraz chodzi o to, że możemy zadeklarować statyczne pełne metody w interfejsie i możemy wykonać interfejs, deklarując główną metodę wewnątrz interfejsu

interface Demo 
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}

0

Idea posiadania abstrakcyjnej metody statycznej polegałaby na tym, że nie można użyć tej konkretnej klasy abstrakcyjnej bezpośrednio dla tej metody, ale tylko pierwsza pochodna może zaimplementować tę metodę statyczną (lub dla ogólnych: rzeczywistej klasy ogólnej posługiwać się).

W ten sposób można na przykład utworzyć klasę abstrakcyjną sortableObject lub nawet interfejs z (automatycznymi) abstrakcyjnymi metodami statycznymi, która określa parametry opcji sortowania:

public interface SortableObject {
    public [abstract] static String [] getSortableTypes();
    public String getSortableValueByType(String type);
}

Teraz możesz zdefiniować sortowalny obiekt, który można sortować według głównych typów, które są takie same dla wszystkich tych obiektów:

public class MyDataObject implements SortableObject {
    final static String [] SORT_TYPES = {
        "Name","Date of Birth"
    }
    static long newDataIndex = 0L ;

    String fullName ;
    String sortableDate ;
    long dataIndex = -1L ;
    public MyDataObject(String name, int year, int month, int day) {
        if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
        if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
        this.fullName = name ;
        this.sortableDate = MyUtils.createSortableDate(year,month,day);
        this.dataIndex = MyDataObject.newDataIndex++ ;
    }
    public String toString() {
        return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
    }

    // override SortableObject 
    public static String [] getSortableTypes() { return SORT_TYPES ; }
    public String getSortableValueByType(String type) {
        int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
        switch(index) {
             case 0: return this.name ;
             case 1: return this.sortableDate ;
        }
        return toString(); // in the order they were created when compared
    }
}

Teraz możesz utworzyć

public class SortableList<T extends SortableObject> 

który może pobierać typy, zbudować menu podręczne, aby wybrać typ do sortowania i posortować listę, pobierając dane z tego typu, a także uzyskać funkcję dodawania, która po wybraniu typu sortowania może automatycznie -sortuj nowe elementy w. Zauważ, że instancja SortableList może bezpośrednio uzyskać dostęp do metody statycznej „T”:

String [] MenuItems = T.getSortableTypes();

Problem z koniecznością użycia instancji polega na tym, że SortableList może jeszcze nie zawierać elementów, ale już musi zapewnić preferowane sortowanie.

Cheerio, Olaf.


0

Po pierwsze, kluczowy punkt dotyczący klas abstrakcyjnych - nie można utworzyć instancji klasy abstrakcyjnej (patrz wiki ). Dlatego nie można utworzyć żadnego wystąpienia klasy abstrakcyjnej.

Teraz sposób, w jaki Java radzi sobie z metodami statycznymi, polega na udostępnianiu metody wszystkim instancjom tej klasy.

Jeśli więc nie można utworzyć instancji klasy, klasa ta nie może mieć abstrakcyjnych metod statycznych, ponieważ metoda abstrakcyjna zaczyna być rozszerzana.

Bum.


Każda metoda w klasie abstrakcyjnej musi być mimo to rozszerzona, aby mogła zostać wykonana, więc nie jest to wymówka. Możesz rozszerzyć klasę abstrakcyjną (jednocześnie implementując metodę abstrakcyjną). Więc to nie działa jako wyjaśnienie.
Pere

0

Zgodnie z dokumentacją Java :

Metoda statyczna to metoda powiązana z klasą, w której jest zdefiniowana, a nie z dowolnym obiektem. Każda instancja klasy ma wspólne metody statyczne

W Javie 8 wraz z domyślnymi metodami dozwolone są również metody statyczne w interfejsie. Ułatwia nam to organizowanie metod pomocniczych w naszych bibliotekach. Możemy zachować metody statyczne specyficzne dla interfejsu w tym samym interfejsie, a nie w osobnej klasie.

Dobrym przykładem tego jest:

list.sort(ordering);

zamiast

Collections.sort(list, ordering);

Inny przykład użycia metod statycznych podano również w samym dokumencie :

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

0

Ponieważ „streszczenie” oznacza, że ​​metoda ma być nadpisana i nie można zastąpić metod „statycznych”.


Ta odpowiedź nie dodaje niczego, czego poprzednie odpowiedzi jeszcze nie dotyczyły.
MarsAtomic

@MarsAtomic Myślę, że to bardziej odpowiednie niż najlepiej głosowana odpowiedź. Jest też zwięzły.
Praveen Kumar

Następnie możesz edytować wcześniejsze odpowiedzi, aby je poprawić, gdy masz wystarczającą liczbę powtórzeń. Wszystko, co robisz, to dodawanie szumu do sygnału poprzez tworzenie duplikatów odpowiedzi. Postępuj zgodnie z ustalonymi regułami i zwyczajami stosu przepełnienia stosu, zamiast tworzyć własne i oczekiwać, że wszyscy inni będą ich przestrzegać.
MarsAtomic

Nie zgadzam się, proszę porównać moją odpowiedź z innymi, szczególnie z najlepszą odpowiedzią głosowaną.
Praveen Kumar

„Dlaczego nie mogę tego zrobić?” Odpowiedź: „bo nie możesz”.
JacksOnF1re

0

Zwykłe metody mogą być abstrakcyjne, gdy mają być zastąpione przez podklasy i wyposażone w funkcje. Wyobraź sobie, że klasa Foojest przedłużonaBar1, Bar2, Bar3 itd. Tak więc każda z nich będzie miała własną wersję klasy abstrakcyjnej zgodnie z własnymi potrzebami.

Otóż ​​metody statyczne z definicji należą do klasy, nie mają one nic wspólnego z obiektami klasy ani obiektami jej podklas. Nie potrzebują nawet ich istnienia, można z nich korzystać bez tworzenia instancji klas. Dlatego muszą być gotowe do pracy i nie mogą zależeć od podklas, aby dodać im funkcjonalność.


0

Ponieważ streszczenie jest słowem kluczowym stosowanym nad metodami abstrakcyjnymi, nie określają treści. A jeśli mówimy o statycznym słowie kluczowym, należy ono do obszaru klasy.


proszę nieco rozwinąć swoją odpowiedź.
Blip

0

ponieważ jeśli używasz w klasie dowolnego elementu statycznego lub zmiennej statycznej, ładuje się on podczas ładowania klasy.


3
i dlaczego miałby to być problem?
eis

-1

Możesz to zrobić za pomocą interfejsów w Javie 8.

Oto oficjalna dokumentacja na ten temat:

https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html


2
W jaki sposób? Szukałem rozwiązań i nie mogłem ich znaleźć.
thouliha

Wut Nie możesz. Wszystkie metody interfejsów statycznych muszą być wywoływane przy użyciu klasy interfejsu.
mmm,

8
Ta odpowiedź jest błędna i wprowadza w błąd ludzi nowych w Javie. Ma to na celu pokazanie dowolnego przykładu abstrakcyjnej metody statycznej, ponieważ nie można jej zaimplementować i podać w innych odpowiedziach
Blip

-1

Ponieważ jeśli klasa rozszerza klasę abstrakcyjną, musi zastąpić metody abstrakcyjne, co jest obowiązkowe. A ponieważ metody statyczne są metodami klasy rozstrzyganymi w czasie kompilacji, podczas gdy metody zastępowane są metodami instancji rozwiązywanymi w czasie wykonywania i po dynamicznym polimorfizmie.


Kapituluj i interpunktuj ten bałagan.
Markiz Lorne
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.