Dlaczego nie mogę zdefiniować metody statycznej w interfejsie Java?

498

EDYCJA: Od Java 8 metody statyczne są teraz dozwolone w interfejsach.

Oto przykład:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

Oczywiście, że to nie zadziała. Ale dlaczego nie?

Jednym z możliwych problemów może być to, co dzieje się, gdy zadzwonisz:

IXMLizable.newInstanceFromXML(e);

W tym przypadku myślę, że powinien wywołać pustą metodę (tj. {}). Wszystkie podklasy byłyby zmuszone do wdrożenia metody statycznej, więc wszystkie byłyby w porządku podczas wywoływania metody statycznej. Dlaczego to nie jest możliwe?

EDYCJA: Wydaje mi się, że szukam odpowiedzi głębszej niż „ponieważ taka jest Java”.

Czy istnieje szczególny technologiczny powód, dla którego metody statyczne nie mogą zostać zastąpione? To znaczy, dlaczego projektanci Java postanowili nadpisać metody instancji, ale nie metody statyczne?

EDYCJA: Problem z moim projektem polega na tym, że próbuję użyć interfejsów w celu wymuszenia konwencji kodowania.

Oznacza to, że cel interfejsu jest dwojaki:

  1. Chcę, aby interfejs IXMLizable umożliwiał mi konwersję klas, które implementują go na elementy XML (przy użyciu polimorfizmu działa dobrze).

  2. Jeśli ktoś chce stworzyć nową instancję klasy, która implementuje interfejs IXMLizable, zawsze będzie wiedział, że będzie nowy konstruktor statycznyInstanceFromXML (Element e).

Czy jest jakiś inny sposób, aby to zapewnić, poza umieszczeniem komentarza w interfejsie?

cdmckay
źródło
4
Nie trzeba zaśmiecać definicji metod (i pól) publicznie w interfejsach, btw.
Tom Hawtin - tackline
Hmm, wydaje się być duplikatem stackoverflow.com/questions/21817/… . Nie widziałem tego wcześniej.
Michael Myers
1
Czy możesz podać kod, w jaki sposób chcesz używać statycznych metod interfejsu?
Pavel Feldman
43
Będzie to możliwe w Javie 8: docs.oracle.com/javase/tutorial/java/IandI/…
dakshang
1
@dakshang Tak, ale nie robi tego, co chce OP.
user253751,

Odpowiedzi:

518

Java 8 zezwala na metody interfejsu statycznego

W Javie 8 interfejsy mogą mieć metody statyczne. Mogą także mieć konkretne metody instancji, ale nie pola instancji.

Są tutaj naprawdę dwa pytania:

  1. Dlaczego w dawnych czasach interfejsy nie mogły zawierać metod statycznych?
  2. Dlaczego nie można zastąpić metod statycznych?

Metody statyczne w interfejsach

Nie było silnego technicznego powodu, dla którego interfejsy nie mogły mieć metod statycznych w poprzednich wersjach. Ładnie to podsumowuje plakat z duplikatem pytania. Metody interfejsu statycznego były początkowo uważane za małą zmianę języka, a następnie pojawiła się oficjalna propozycja dodania ich w Javie 7, ale później porzucona z powodu nieprzewidzianych komplikacji.

Wreszcie, Java 8 wprowadziła metody interfejsu statycznego, a także metody instancji z możliwością zastąpienia z domyślną implementacją. Nadal nie mogą mieć pól instancji. Te funkcje są częścią obsługi wyrażeń lambda i możesz przeczytać o nich więcej w części H JSR 335.

Przesłanianie metod statycznych

Odpowiedź na drugie pytanie jest nieco bardziej skomplikowana.

Metody statyczne można rozwiązać w czasie kompilacji. Dynamiczne wysyłanie ma sens na przykład w przypadku metod, w których kompilator nie może określić konkretnego typu obiektu, a zatem nie może rozwiązać metody, która ma zostać wywołana. Jednak wywołanie metody statycznej wymaga klasy, a ponieważ klasa ta jest znana statycznie - w czasie kompilacji - dynamiczne wysyłanie nie jest konieczne.

Aby zrozumieć, co się tutaj dzieje, potrzebne jest trochę tła na temat działania metod instancji. Jestem pewien, że rzeczywista implementacja jest zupełnie inna, ale pozwól mi wyjaśnić moje pojęcie wysyłki metody, które modele dokładnie obserwowały zachowanie.

Udawaj, że każda klasa ma tablicę skrótów, która odwzorowuje sygnatury metod (nazwy i typy parametrów) na rzeczywistą część kodu, aby zaimplementować metodę. Gdy maszyna wirtualna próbuje wywołać metodę w instancji, sprawdza obiekt w swojej klasie i wyszukuje żądany podpis w tabeli klasy. Jeśli zostanie znalezione ciało metody, zostanie ono wywołane. W przeciwnym razie uzyskuje się klasę nadrzędną klasy i tam wyszukiwanie jest powtarzane. Dzieje się tak, dopóki nie zostanie znaleziona metoda lub nie będzie już klas nadrzędnych - w wyniku czego powstanieNoSuchMethodError .

Jeśli zarówno nadklasa, jak i podklasa mają w swoich tabelach wpis dotyczący tej samej sygnatury metody, najpierw pojawia się wersja podklasy, a wersja nadklasy nigdy nie jest używana - jest to „przesłonięcie”.

Załóżmy teraz, że pomijamy instancję obiektu i zaczynamy od podklasy. Rozdzielczość może przebiegać jak wyżej, dając ci rodzaj „nadpisywalnej” metody statycznej. Rozdzielczość może się jednak zdarzyć w czasie kompilacji, ponieważ kompilator zaczyna od znanej klasy, zamiast czekać na środowisko wykonawcze w celu zapytania do obiektu o nieokreślonym typie dla swojej klasy. Nie ma sensu „zastępować” metody statycznej, ponieważ zawsze można określić klasę, która zawiera żądaną wersję.


„Interfejsy” konstruktora

Oto trochę więcej materiałów na temat ostatniej edycji pytania.

Wygląda na to, że chcesz skutecznie zlecić metodę podobną do konstruktora dla każdej implementacji IXMLizable. Zapomnij na chwilę o wymuszeniu tego za pomocą interfejsu i udawaj, że masz klasy, które spełniają to wymaganie. Jak byś tego użył?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Ponieważ musisz wyraźnie nazwać konkretny typ Foopodczas „konstruowania” nowego obiektu, kompilator może sprawdzić, czy rzeczywiście ma on niezbędną metodę fabryczną. A jeśli nie, to co z tego? Jeśli mogę zaimplementować taki, IXMLizablektóry nie ma „konstruktora”, i utworzę instancję i przekażę ją do twojego kodu, będzie on miał IXMLizableniezbędny interfejs.

Budowa jest częścią implementacji, a nie interfejsu. Każdy kod, który działa poprawnie z interfejsem, nie dba o konstruktora. Każdy kod, który dba o konstruktor, musi znać konkretny typ, a interfejs można zignorować.

erickson
źródło
Dobre wezwanie na Project Coin. mail.openjdk.java.net/pipermail/coin-dev/2009-March/000117.html
Michael Myers
12
Czy przyczyną nr 1 może być wielokrotne dziedziczenie? Ponieważ możemy dziedziczyć z wielu interfejsów, jeśli dwa interfejsy będą zawierały tę samą sygnaturę metody statycznej, a następnie klasa zaimplementuje je obie i wywoła tę metodę, wówczas sprawy mogą się skomplikować w sposób, którego twórcy języka Java chcieli uniknąć, uniemożliwiając dziedziczenie wielu klas w pierwsze miejsce. Ten sam argument można oczywiście wysunąć, aby interfejs w ogóle nie pozwalał na definicję żadnej metody.
shrini1000
1
@ shrini1000 - Nie, metody statyczne są rozwiązywane w czasie kompilacji. Dwuznaczność może być obsługiwana w taki sam sposób, jak w przypadku stałych: z błędem kompilatora. Jednak propozycja w ramach projektu Coin została odrzucona, powołując się na pewne nieprzewidziane trudności. Nie jestem pewien, co to było, ale nie sądzę, żeby było tak.
erickson
1
@ tgm1024 Tak, sekcja „Interfejsy konstruktora” wyjaśnia, dlaczego nie ma sensu próbować wywoływać zachowania polimorficznego za pomocą typu znanego w czasie kompilacji. Jak byś przywołał RESET()na danej klasie? Można by napisać SomeClass.RESET(). Więc nie potrzebujesz interfejsu do opisania tego API; jest statyczny. Interfejsy są używane, gdy nie znasz konkretnego typu w czasie kompilacji. Tak nigdy nie jest w przypadku metody statycznej.
erickson
2
„Budowa jest częścią implementacji, a nie interfejsu. Każdy kod, który działa poprawnie z interfejsem, nie dba o konstruktora.” - To oczywiście nieprawda. W innych językach (np. Swift) mogę tworzyć nowe instancje Tbez wiedzy Tstatycznej, ponieważ obiecuję w interfejsie, że określony konstruktor (lub metoda statyczna) będzie istniał w czasie wykonywania. Fakt, że generowanie jest niemożliwe do określenia w Javie, nie oznacza, że ​​nie jest to sensowne.
Raphael
48

To pytanie zostało już zadane i udzielono odpowiedzi tutaj

Aby powielić moją odpowiedź:

Nigdy nie ma sensu deklarować metody statycznej w interfejsie. Nie można ich wykonać za pomocą zwykłego wywołania MyInterface.staticMethod (). Jeśli wywołasz je, podając klasę implementacyjną MyImplementor.staticMethod (), musisz znać rzeczywistą klasę, więc nie ma znaczenia, czy interfejs ją zawiera, czy nie.

Co ważniejsze, metody statyczne nigdy nie są zastępowane, a jeśli spróbujesz:

MyInterface var = new MyImplementingClass();
var.staticMethod();

reguły statyczne mówią, że należy wykonać metodę zdefiniowaną w zadeklarowanym typie zmiennej. Ponieważ jest to interfejs, jest to niemożliwe.

Powodem, dla którego nie można wykonać polecenia „result = MyInterface.staticMethod ()” jest to, że musiałby wykonać wersję metody zdefiniowanej w MyInterface. Ale nie można zdefiniować wersji w MyInterface, ponieważ jest to interfejs. Z definicji nie ma kodu.

Chociaż można powiedzieć, że sprowadza się to do „ponieważ Java robi to w ten sposób”, w rzeczywistości decyzja jest logiczną konsekwencją innych decyzji projektowych, również podjętych z bardzo ważnego powodu.

DJClayworth
źródło
14
Jeśli użyjesz <T extends MyInterface> jako parametru typu ogólnego, dobrze byłoby zagwarantować za pośrednictwem interfejsu, że T może .doSomething ().
Chris Betti,
4
Chociaż rozumiem argumenty, zgadzam się z @Chris_Betti (nawet w przypadku typów innych niż ogólne): byłoby miło, że struktura kodu zapewnia, że ​​niektóre klasy implementują określony statyczny interfejs API. Może jest to możliwe przy użyciu innej koncepcji ...
Juh_
@Juh_, ..... lub nowe słowo kluczowe, jeśli jest absolutnie wymagane. Uważam, że i tak staticjest kiepskim terminem w językach i został rozciągnięty zbyt daleko. Samo to jest już szkicowe. Zobacz mój przykład powyżej stackoverflow.com/questions/512877/... {wzruszenie ramion}.
3
Wydaje się to nieprawdziwe: „Nigdy nie ma sensu deklarować metody statycznej w interfejsie”. Jeśli mam kolekcję klas, które bez tworzenia instancji mogłyby zaoferować mi pewne informacje, ale potrzebowałbym wspólnego interfejsu do umieszczenia tych informacji na poziomie klasy statycznej (tj. Interfejsu z nadpisywalną metodą statyczną), to jest to prawidłowe zastosowanie . Pomyśl odbicie ++, w którym możesz przechwytywać meta-informacje o właściwościach klasy bez włamywania się za pomocą atrybutów, odbicia itp.
Jason
1
„Nigdy nie ma sensu deklarować metody statycznej w interfejsie”. To nie jest prawda: wyobraź sobie, że twój system ma domyślny resolver klasy. Jeśli wykryje, że zaimplementujesz metodę ContainerInjectionInterce :: create (Container $ container), to na przykład utworzy obiekt za pomocą tej funkcji.
user3790897,
37

Zwykle odbywa się to za pomocą wzoru fabrycznego

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}
Peter Lawrey
źródło
7
+1 wzór fabryczny brzmi jak rozwiązanie problemu. (choć nie do pytania)
pvgoddijn
Czy ktoś może mi powiedzieć, co oznacza umieszczenie <T rozszerza IXMLizable> tutaj. Jestem nowy w java. Co to robi?
Nuwan Harshakumara Piyarathna
1
@NuwanHarshakumaraPiyarathna T musi być klasą rozszerzającą IXMLizable. Przyjrzyj się rodzajom języka Java, aby lepiej zrozumieć, co to oznacza
Adriana
37

Wraz z pojawieniem się Java 8 możliwe jest teraz pisanie domyślnych i statycznych metod w interfejsie. docs.oracle/staticMethod

Na przykład:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

Wynik : 6

WSKAZÓWKA: Wywołanie metody interfejsu statycznego nie wymaga implementacji przez żadną klasę. Z pewnością dzieje się tak, ponieważ te same reguły dla metod statycznych w superklasach mają zastosowanie do metod statycznych na interfejsach.

nieuczciwy chłopak
źródło
To doskonały przykład tego pytania.
21

Ponieważ metod statycznych nie można zastąpić w podklasach, a zatem nie mogą być abstrakcyjne. Wszystkie metody interfejsu są de facto abstrakcyjne.

Michael Myers
źródło
2
Zawsze możesz wymusić na każdym typie wdrożenie dowolnej metody interfejsu statycznego. Typy, ktoś?
MichaelGG
16
Wyjdź poza siebie i odpowiedz na pytanie: Dlaczego nie można zastąpić metod statycznych? Gdyby można było zastąpić metody statyczne, jak by to wyglądało? Co możesz z nimi zrobić? Ta odpowiedź brzmi „nie możesz, bo nie możesz”.
erickson
10

Dlaczego nie mogę zdefiniować metody statycznej w interfejsie Java?

Właściwie możesz w Javie 8.

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 interfejs może mieć metody domyślne i metody statyczne . 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.

Przykład domyślnej metody:

list.sort(ordering);

zamiast

Collections.sort(list, ordering);

Przykład metody statycznej (z samego dokumentu ):

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));
    }    
}
akhil_mittal
źródło
6

Interfejsy dotyczą polimorfizmu, który jest nieodłącznie związany z instancjami obiektów, a nie klasami. Dlatego statyczny nie ma sensu w kontekście interfejsu.

cliff.meyers
źródło
Jasna, zwięzła logika. Dobrze wyłożone.
Oke Uwechue
6

Po pierwsze, wszystkie decyzje językowe są decyzjami podejmowanymi przez twórców języków. W świecie inżynierii oprogramowania, definiowania języka lub pisania kompilatora / interpretera nie ma nic, co mówi, że metoda statyczna nie może być częścią interfejsu. Stworzyłem dla nich kilka języków i napisałem dla nich kompilatory - wszystko to po prostu siedzi i określa znaczącą semantykę. Twierdziłbym, że semantyka metody statycznej w interfejsie jest wyjątkowo jasna - nawet jeśli kompilator musi odroczyć rozdzielczość metody w czasie wykonywania.

Po drugie, że w ogóle używamy metod statycznych, oznacza to, że istnieje prawidłowy powód posiadania wzorca interfejsu, który obejmuje metody statyczne - nie mogę mówić za żadnego z was, ale regularnie używam metod statycznych.

Najbardziej prawdopodobną prawidłową odpowiedzią jest to, że w czasie definiowania języka nie było zauważalnej potrzeby stosowania metod statycznych w interfejsach. Java znacznie się rozwinęła przez lata i jest to przedmiot, który najwyraźniej zyskał zainteresowanie. To, że zostało sprawdzone dla Javy 7, wskazuje, że wzrósł on do poziomu zainteresowania, który może spowodować zmianę języka. Ja, na przykład, będę szczęśliwy, gdy nie będę już musiał tworzyć instancji obiektu, aby móc wywołać moją metodę niestatystyczną gettera, aby uzyskać dostęp do zmiennej statycznej w instancji podklasy ...

wysoka dziewczyna
źródło
5

Metody statyczne nie są wirtualne jak metody instancji, więc przypuszczam, że projektanci Java zdecydowali, że nie chcą ich w interfejsach.

Ale możesz umieścić klasy zawierające metody statyczne w interfejsach. Możesz tego spróbować!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
Adrian Pronk
źródło
5
  • „Czy istnieje szczególny powód, dla którego metody statyczne nie mogą zostać zastąpione”.

Pozwólcie, że przeredaguję to pytanie, wypełniając definicje.

  • „Czy istnieje szczególny powód, dla którego metody rozwiązane w czasie kompilacji nie mogą być rozwiązane w czasie wykonywania.”

Lub, mówiąc dokładniej, jeśli chcę wywołać metodę bez instancji, ale znając klasę, jak mogę ją rozwiązać na podstawie instancji, której nie mam.

Darron
źródło
3

W kilku odpowiedziach omówiono problemy z koncepcją nadpisywalnych metod statycznych. Czasami jednak natrafiasz na wzór, w którym wydaje się, że właśnie tego chcesz użyć.

Na przykład pracuję z warstwą obiektowo-relacyjną, która ma obiekty wartości, ale także polecenia do manipulowania obiektami wartości. Z różnych powodów każda klasa obiektu wartości musi zdefiniować niektóre metody statyczne, które pozwalają ramce znaleźć instancję polecenia. Na przykład, aby utworzyć osobę, którą zrobiłbyś:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

i aby załadować osobę według identyfikatora, który zrobisz

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

Jest to dość wygodne, jednak ma swoje problemy; zwłaszcza interfejs nie może wymuszać istnienia metod statycznych. Nadrzędna metoda statyczna w interfejsie byłaby dokładnie tym, czego potrzebowalibyśmy, gdyby tylko jakoś działała.

EJB rozwiązują ten problem, mając interfejs domowy; każdy obiekt wie, jak znaleźć swój Dom, a Dom zawiera metody „statyczne”. W ten sposób metody „statyczne” można w razie potrzeby zastąpić i nie zaśmiecać normalnego interfejsu (nazywa się to „zdalnym”) metodami, które nie dotyczą instancji komponentu bean. Wystarczy, że normalny interfejs określi metodę „getHome ()”. Zwraca instancję obiektu Home (który, jak sądzę, może być singletonem), a osoba dzwoniąca może wykonywać operacje, które wpływają na wszystkie obiekty Person.

Mr. Shiny and New 安 宇
źródło
3

Komentowanie EDIT: As of Java 8, static methods are now allowed in interfaces.

To prawda, statyczne metody, ponieważ Java 8 są dozwolone w interfejsach, ale twój przykład nadal nie działa. Nie możesz po prostu zdefiniować metody statycznej: musisz ją zaimplementować lub otrzymasz błąd kompilacji.

Flavio
źródło
2

Cóż, bez ogólnych, statyczne interfejsy są bezużyteczne, ponieważ wszystkie statyczne wywołania metod są rozstrzygane w czasie kompilacji. Więc nie ma dla nich prawdziwego zastosowania.

W przypadku generycznych mają zastosowanie - z domyślną implementacją lub bez niej. Oczywiście musiałoby to być nadrzędne i tak dalej. Jednak przypuszczam, że takie użycie nie było bardzo OO (jak wskazują inne odpowiedzi tępo), a zatem nie było uważane za warte wysiłku, który wymagałoby wdrożenia.

MichaelGG
źródło
1
Co mają z tym wspólnego leki generyczne? Metoda statyczna interfejsu nadal byłaby niewykonalna.
DJClayworth,
Po pierwsze, byłaby to decyzja wdrożeniowa. Ale zgaduję, że nie chce wywoływać metod statycznych na interfejsie (mógłby po prostu użyć klasy). Ale zamiast tego chce mieć coś w rodzaju rodzaju czcionki lub czegoś więcej niż parametrów typu. W rzeczywistości jego najnowsza edycja pokazuje to jeszcze wyraźniej.
MichaelGG
2
Why can't I define a static method in a Java interface?

Wszystkie metody w interfejsie są jawnie abstrakcyjne, dlatego nie można ich zdefiniować jako statycznych, ponieważ metody statyczne nie mogą być abstrakcyjne.

Aniket Thakur
źródło
1

Interfejsu nigdy nie można wyrejestrować statycznie, np ISomething.member. Interfejs jest zawsze wyłuskiwany przez zmienną, która odnosi się do instancji podklasy interfejsu. Zatem odwołanie interfejsu nigdy nie może wiedzieć, do której podklasy się odwołuje, bez wystąpienia jego podklasy.

Zatem najbliższym przybliżeniem do metody statycznej w interfejsie byłaby metoda niestatyczna, która ignoruje „to”, tj. Nie uzyskuje dostępu do żadnych niestatycznych elementów instancji. W przypadku abstrakcji niskiego poziomu każda metoda niestatyczna (po wyszukaniu w dowolnej tabeli vt) jest tak naprawdę tylko funkcją o zakresie klasy, która przyjmuje „to” jako domyślny parametr formalny. Zobacz pojedynczy obiekt Scali i interoperacyjność z Javą jako dowód tej koncepcji. Dlatego każda metoda statyczna jest funkcją o zasięgu klasy, która nie przyjmuje parametru „this”. W ten sposób zwykle można wywoływać metodę statyczną, ale jak wcześniej wspomniano, interfejs nie ma implementacji (jest abstrakcyjny).

Zatem, aby uzyskać najbliższe przybliżenie do metody statycznej w interfejsie, należy zastosować metodę niestatyczną, a następnie nie uzyskiwać dostępu do żadnego z elementów niestatycznej instancji. Nie byłoby żadnej możliwej korzyści z wydajności w żaden inny sposób, ponieważ nie ma możliwości statycznego połączenia (w czasie kompilacji) a ISomething.member(). Jedyną korzyścią, jaką widzę w przypadku metody statycznej w interfejsie, jest to, że nie wprowadzałaby (tzn. Ignorowała) niejawne „to”, a tym samym uniemożliwiała dostęp do któregokolwiek z elementów instancji niestatycznej. Oznaczałoby to domyślnie, że funkcja, która nie ma dostępu do „tego”, jest niezmienna, a nawet nie tylko w odniesieniu do jej zawierającej klasy. Ale deklaracja „statyczna” w interfejsie ISomethingdezorientowałaby także ludzi, którzy próbowali uzyskać do niej dostępISomething.member()co spowodowałoby błąd kompilatora. Podejrzewam, że jeśli błąd kompilatora byłby wystarczająco wyjaśniający, byłoby lepiej niż próbować edukować ludzi na temat korzystania z metody niestatycznej, aby osiągnąć to, czego chcą (najwyraźniej głównie metody fabryczne), tak jak my to robimy (i powtarzano to przez 3 Pytania i odpowiedzi na tej stronie), więc jest to oczywiście problem, który nie jest intuicyjny dla wielu osób. Musiałem się nad tym zastanowić, aby uzyskać właściwe zrozumienie.

Aby uzyskać zmienne pole statyczne w interfejsie, należy użyć niestatystycznych metod pobierających i ustawiających w interfejsie, aby uzyskać dostęp do tego pola statycznego w podklasie. Sidenote, pozornie niezmienna statystyka może być zadeklarowana w interfejsie Java za pomocą static final.

Shelby Moore III
źródło
0

Interfejsy po prostu dostarczają listę rzeczy, które zapewni klasa, a nie rzeczywistą implementację tych rzeczy, co jest twoim statycznym przedmiotem.

Jeśli chcesz statyki, użyj klasy abstrakcyjnej i odziedzicz ją, w przeciwnym razie usuń statyczną.

Mam nadzieję, że to pomaga!

samoz
źródło
2
Teoretycznie można zdefiniować interfejs, który będzie zawierał zachowanie statyczne, to znaczy „implementacje tego interfejsu będą miały statyczną metodę foo () z tą sygnaturą” i pozostawić implementację konkretnej klasie. Zetknąłem się z sytuacjami, w których takie zachowanie byłoby przydatne.
Rob
0

Nie można zdefiniować metod statycznych w interfejsie, ponieważ metody statyczne należą do klasy, a nie do instancji klasy, a interfejsy nie są klasami. Przeczytaj więcej tutaj.

Jeśli jednak chcesz, możesz to zrobić:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

W tym przypadku masz dwie klasy z 2 odrębnymi metodami statycznymi o nazwie methodX ().

Handerson
źródło
0

Załóżmy, że możesz to zrobić; rozważ ten przykład:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}
pvgoddijn
źródło
1
Napisałby „Jestem klasy A”. Jednak jeśli wpiszesz A.thisIsTheMethod(), wydrukuje „Jestem klasy B”.
cdmckay
ale czy wywołanie metod w interfejsie skąd ty (lub kompilator) wiedziałbyś, którą metodę należy wywołać? (pamiętaj, że może być więcej klas, które dracetycznie wdrażają Iface
pvgoddijn
przepraszam, chciałem powiedzieć: Jeśli jednak wpiszesz B.thisIsTheMethod(), wydrukuje „Jestem klasy B”.
cdmckay
powiedziałem IFace.thisIsTHeMethod celowo, ponieważ na tym polega problem. wywołanie go na interfejsie nie byłoby możliwe bez niezdefiniowanego zachowania (nawet jeśli zostało to zadeklarowane)
pvgoddijn
0

Coś, co można zaimplementować, to interfejs statyczny (zamiast metody statycznej w interfejsie). Wszystkie klasy implementujące dany interfejs statyczny powinny implementować odpowiednie metody statyczne. Możesz uzyskać statyczny interfejs SI z dowolnego używanego klasy

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

wtedy możesz zadzwonić si.method(params). Byłoby to przydatne (na przykład w przypadku fabrycznego wzorca projektowego), ponieważ można uzyskać (lub sprawdzić implementację) implementację metod statycznych SI z nieznanej klasy czasu kompilacji! Dynamiczna wysyłka jest konieczna i można przesłonić metody statyczne (jeśli nie ostateczne) klasy, rozszerzając je (wywoływane przez interfejs statyczny). Oczywiście metody te mają dostęp tylko do zmiennych statycznych swojej klasy.

Christophe Bouchon
źródło
0

Chociaż zdaję sobie sprawę, że Java 8 rozwiązuje ten problem, pomyślałem, że wkroczę do scenariusza, nad którym obecnie pracuję (zablokowany przy użyciu Java 7), w którym pomocne byłoby określenie metod statycznych w interfejsie.

Mam kilka definicji wyliczenia, w których zdefiniowałem pola „id” i „displayName” wraz z metodami pomocniczymi oceniającymi wartości z różnych powodów. Implementacja interfejsu pozwala mi upewnić się, że metody gettera są na miejscu, ale nie metody statycznego pomocnika. Będąc wyliczeniem, naprawdę nie ma czystego sposobu na przeniesienie metod pomocniczych do odziedziczonej klasy abstrakcyjnej lub czegoś podobnego, więc metody muszą być zdefiniowane w samym wyliczeniu. Ponieważ jest to wyliczenie, nigdy nie byłbyś w stanie przekazać go jako obiektu instancji i traktować go jako typ interfejsu, ale lubię istnieć metody statycznego pomocnika przez interfejs, co lubię jest obsługiwany w Javie 8.

Oto kod ilustrujący mój punkt widzenia.

Definicja interfejsu:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

Przykład jednej definicji wyliczenia:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

Ogólna definicja narzędzia wyliczeniowego:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}
Ryan
źródło
0

Załóżmy, że w interfejsach dozwolone są metody statyczne: * Zmusiłyby wszystkie klasy implementujące do zadeklarowania tej metody. * Interfejsy byłyby zwykle używane przez obiekty, więc jedynymi skutecznymi metodami byłyby metody niestatyczne. * Każda klasa, która zna określony interfejs, może wywoływać swoje metody statyczne. Stąd metoda statyczna klasy implementującej zostałaby wywołana poniżej, ale klasa wywołująca nie wie, która. Jak to wiedzieć? Zgadnij, że nie ma instancji!

Uważano, że interfejsy są używane podczas pracy z obiektami. W ten sposób obiekt jest tworzony z określonej klasy, więc ta ostatnia sprawa została rozwiązana. Klasa wywołująca nie musi wiedzieć, która konkretna klasa, ponieważ tworzenie instancji może być wykonane przez trzecią klasę. Klasa wywołująca zna tylko interfejs.

Jeśli chcemy, aby zostało to rozszerzone na metody statyczne, powinniśmy mieć możliwość wcześniejszego sprecyzowania klasy implementującej, a następnie przekazania odwołania do klasy wywołującej. Może to wykorzystać klasę za pomocą metod statycznych w interfejsie. Ale czym różni się to odniesienie od obiektu? Potrzebujemy tylko obiektu reprezentującego klasę. Teraz obiekt reprezentuje starą klasę i mógłby zaimplementować nowy interfejs, w tym stare metody statyczne - teraz są one niestatyczne.

Metaklasy służą do tego celu. Możesz wypróbować klasę Java. Problem polega jednak na tym, że Java nie jest na to wystarczająco elastyczna. Nie można zadeklarować metody w obiekcie klasy interfejsu.

To jest problem z meta - kiedy musisz zrobić tyłek

..bla bla

w każdym razie masz łatwe obejście - dzięki czemu metoda jest niestatyczna z tą samą logiką. Ale najpierw musisz utworzyć obiekt, aby wywołać metodę.

beibichunai
źródło
0

Aby rozwiązać ten problem: błąd: brak metody lub zadeklaruj streszczenie static void main (String [] args);

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

wyjście: 20

Teraz możemy użyć metody statycznej w interfejsie

Aashish Pawar
źródło
1
To jednak bezużyteczne. Zdefiniowanie metody statycznej w interfejsie nie wymusza dalszych definicji w klasach, które implementują taki interfejs. Jeśli po prostu całkowicie usuniesz metodę statyczną z interfejsu I, Twój kod skompiluje się i uruchomi bez problemu. Innymi słowy, NIE zastępujesz głównej metody interfejsu I w klasie InterDemo, po prostu tworzysz nową metodę z tym samym podpisem.
Fran Marzoa
-2

Myślę, że java nie ma statycznych metod interfejsu, ponieważ nie są one potrzebne. Możesz myśleć, że tak, ale ... Jak byś ich użył? Jeśli chcesz do nich zadzwonić jak

MyImplClass.myMethod()

to nie musisz deklarować tego w interfejsie. Jeśli chcesz do nich zadzwonić jak

myInstance.myMethod()

to nie powinno być statyczne. Jeśli faktycznie zamierzasz użyć pierwszego sposobu, ale po prostu chcesz wymusić na każdej implementacji taką statyczną metodę, to tak naprawdę jest to konwencja kodowania, a nie umowa między instancją, która implementuje interfejs i kod wywołujący.

Interfejsy pozwalają zdefiniować kontrakt między instancją klasy, która implementuje interfejs i kod wywołujący. A java pomaga mieć pewność, że ta umowa nie zostanie naruszona, więc możesz na niej polegać i nie martw się, która klasa implementuje tę umowę, wystarczy „ktoś, kto podpisał umowę”. W przypadku interfejsów statycznych Twój kod

MyImplClass.myMethod()

nie polega na tym, że każda implementacja interfejsu ma tę metodę, więc nie potrzebujesz java, aby być pewnym.

Pavel Feldman
źródło
-5

Jaka jest potrzeba metody statycznej w interfejsie, metody statyczne są używane w zasadzie, gdy nie trzeba tworzyć instancji obiektu, cała idea interfejsu polega na wprowadzeniu koncepcji OOP z wprowadzeniem metody statycznej, którą odwracasz od koncepcji.

VIckyb
źródło