Dlaczego nie mogę zadeklarować metod statycznych w interfejsie?

150

Najwięcej mówi o tym temat - jaki jest powód tego, że metody statyczne nie mogą być deklarowane w interfejsie?

public interface ITest {
    public static String test();
}

Powyższy kod daje mi następujący błąd (przynajmniej w Eclipse): „Niedozwolony modyfikator dla metody interfejsu ITest.test (); dozwolone są tylko publiczne i abstrakcyjne”.

Henrik Paul
źródło
2
Proszę odrzucić odpowiedź Espo, ponieważ zawiera ona błędy. Interfejs ma plik klasy, który może zawierać implementację metody statycznej (jeśli projektant języka Java na to zezwoli), więc nie ma problemu z rozwiązaniem implementacji metody statycznej. Działa dokładnie tak samo, jak w przypadku innych klas statycznych.
Mnementh
w pewnym sensie zgadzam się z odpowiedzią udzieloną przez "erickson" stackoverflow.com/questions/512877/…
Maverick
9
Przy okazji będzie to dostępne w Javie 8.
m0skit0
1
@Vadorequest GIYF, ale tak czy
m0skit0
2
Linki z oficjalnej dokumentacji: samouczek Java SE i specyfikacja języka Java 9.2
LoganMzz

Odpowiedzi:

85

Jest tu kilka problemów. Pierwsza to kwestia zadeklarowania metody statycznej bez jej definiowania. To jest różnica między

public interface Foo {
  public static int bar();
}

i

public interface Foo {
  public static int bar() {
    ...
  }
}

Pierwsza jest niemożliwa z powodów, o których wspomina Espo : nie wiesz, która klasa implementująca jest poprawną definicją.

Java może pozwolić na to drugie; i faktycznie, począwszy od Java 8, tak!

James A. Rosen
źródło
2
Tak - to jest idealogiczne, a nie techniczne. Chciałbym, żeby to było. że można mieć statyczną metodę „implementacji” w interfejsie, która odwołuje się tylko do innych metod „interfejsu” w interfejsie, które mogą być łatwo ponownie użyte przez implementację klas. Ale można zadeklarować klasę statyczną w interfejsie, więc można by tam znajdować się takie rzeczy, jak MyInterface.Impl.doIt (MyInterface i, Object [] args) {...}
peterk
9
Od wersji Java 8 można definiować staticmetody w pliku interface. Metody muszą być public.
Olivier Grégoire
4
@ OlivierGrégoire ... i nie są dziedziczone, co jest kluczowe.
William F. Jameson,
1
Dobra odpowiedź, chociaż „w przybliżeniu odpowiednik” ROFLMAO xD, powiedziałbym raczej „trochę przypomina”.
Timo
44

Przyczyna, dla której nie możesz mieć statycznej metody w interfejsie, leży w sposobie, w jaki Java rozwiązuje statyczne odwołania. Java nie będzie zawracać sobie głowy szukaniem instancji klasy podczas próby wykonania metody statycznej. Dzieje się tak, ponieważ metody statyczne nie są zależne od instancji i dlatego mogą być wykonywane bezpośrednio z pliku klasy. Biorąc pod uwagę, że wszystkie metody w interfejsie są abstrakcyjne, maszyna wirtualna musiałaby szukać konkretnej implementacji interfejsu, aby znaleźć kod znajdujący się za metodą statyczną, aby można było ją wykonać. To zaprzecza temu, jak działa statyczna rozdzielczość metody i wprowadziłoby niespójność do języka.

Espo
źródło
3
To wyjaśnienie nie wyjaśnia problemu. Każdy interfejs ma swój własny plik klasy, może zawierać metodę statyczną. Nie będzie więc konieczne wyszukiwanie konkretnej implementacji.
Mnementh
Nie każdy typ interfejsu w Javie znajduje się w swoim własnym pliku ani nie powinien być zgodny z JLS. Ponadto JLS nie przewiduje, że klasy muszą być zawsze przechowywane w systemie plików, wręcz przeciwnie.
Vlad Gudim
4
@Totophil: Interfejsy nie mogą znajdować się w jednym pliku java, ale po kompilacji będą miały własny plik klasy. Tak właśnie napisałem.
Mnementh
18

Odpowiem na twoje pytanie przykładem. Załóżmy, że mamy klasę Math z dodaną metodą statyczną. Nazwałbyś tę metodę w ten sposób:

Math.add(2, 3);

Gdyby Math był interfejsem zamiast klasą, nie mógłby mieć żadnych zdefiniowanych funkcji. W związku z tym powiedzenie czegoś takiego jak Math.add (2, 3) nie ma sensu.

Kyle Cronin
źródło
11

Przyczyna leży w zasadzie projektowania, że ​​java nie zezwala na wielokrotne dziedziczenie. Problem z dziedziczeniem wielokrotnym można zilustrować następującym przykładem:

public class A {
   public method x() {...}
}
public class B {
   public method x() {...}
}
public class C extends A, B { ... }

Co się teraz stanie, jeśli wywołasz Cx ()? Czy zostanie wykonane Ax () czy Bx ()? Każdy język z wielokrotnym dziedziczeniem musi rozwiązać ten problem.

Interfejsy pozwalają w Javie na pewne ograniczone dziedziczenie wielokrotne. Aby uniknąć powyższego problemu, nie wolno im używać metod. Jeśli spojrzymy na ten sam problem z interfejsami i metodami statycznymi:

public interface A {
   public static method x() {...}
}
public interface B {
   public static method x() {...}
}
public class C implements A, B { ... }

Ten sam problem, co się stanie, jeśli wywołasz Cx ()?

Mnementh
źródło
Czy jest jakiś powód do przegłosowania? Wyjaśniający komentarz byłby miły.
Mnementh
nie jestem przeciwnikiem, ale czy nie jest to również ważne w przypadku metod niestatycznych?
nawfal
OK, oto dwie różne możliwości. Metodę można zaimplementować lub tylko zadeklarować. Zrozumiałem, że trzeba zaimplementować metodę statyczną. W tym sensie napotykam problem przedstawiony w mojej odpowiedzi. Jeśli tego nie zrobisz, napotkasz problem opisany przez Espo - i którego nie rozumiałem, ponieważ zakładałem, że metoda statyczna zostanie zaimplementowana. Z tego powodu nie możesz również zadeklarować statycznej metody abstrakcyjnej, wypróbuj ją, kompilator będzie narzekał.
Mnementh
Ok, zapomnij o części dotyczącej implementacji. pytanie brzmi, dlaczego nie możemy zadeklarować. tak, kompilator będzie narzekał i dlaczego to jest pytanie. na co chyba nie odpowiedziałeś.
nawfal
1
Jaka byłaby gorsza sytuacja niż posiadanie interfejsu Azawierającego int x(int z);i Bzawierającego interfejs string x(int x);? Jakie jest znaczenie x(3)w interfejsie C?
supercat
7

Metody statyczne nie są metodami instancji. Nie ma kontekstu instancji, dlatego implementacja jej z poziomu interfejsu nie ma sensu.

Ryan Farley
źródło
5

Teraz Java8 pozwala nam zdefiniować nawet metody statyczne w interfejsie.

interface X {
    static void foo() {
       System.out.println("foo");
    }
}

class Y implements X {
    //...
}

public class Z {
   public static void main(String[] args) {
      X.foo();
      // Y.foo(); // won't compile because foo() is a Static Method of X and not Y
   }
}

Uwaga: Metody w interfejsie są nadal domyślnie publiczne abstrakcyjne, jeśli nie używamy jawnie słów kluczowych default / static, aby uczynić je metodami domyślnymi i metodami statycznymi.

Anandaraja_Srinivasan
źródło
4

Jest to bardzo miłe i zwięzła odpowiedź na swoje pytanie tutaj . (Wydało mi się to przyjemnym, prostym sposobem wyjaśnienia, że ​​chcę go tutaj połączyć.)

Zarkonnen
źródło
To nie jest odpowiedź na pytanie, w najlepszym przypadku powinien to być komentarz.
CubeJockey
3

Wygląda na to, że metoda statyczna w interfejsie może być obsługiwana w Javie 8 , cóż, moje rozwiązanie polega po prostu na zdefiniowaniu ich w klasie wewnętrznej.

interface Foo {
    // ...
    class fn {
        public static void func1(...) {
            // ...
        }
    }
}

Tej samej techniki można również użyć w adnotacjach:

public @interface Foo {
    String value();

    class fn {
        public static String getValue(Object obj) {
            Foo foo = obj.getClass().getAnnotation(Foo.class);
            return foo == null ? null : foo.value();
        }
    }
}

Dostęp do klasy wewnętrznej należy zawsze uzyskiwać w postaci Interface.fn...zamiast Class.fn..., wtedy można pozbyć się niejednoznacznego problemu.

Xiè Jìléi
źródło
2

Interfejs jest używany do polimorfizmu, który dotyczy obiektów, a nie typów. Dlatego (jak już wspomniano) nie ma sensu mieć statycznego elementu interfejsu.

Rob Cooper
źródło
W niektórych kontekstach refleksyjnych wydaje się jednak, że ma to sens
Cruncher
1

Java 8 Zmieniła świat, możesz mieć statyczne metody w interfejsie, ale to zmusza cię do zapewnienia ich implementacji.

public interface StaticMethodInterface {
public static int testStaticMethod() {
    return 0;
}

/**
 * Illegal combination of modifiers for the interface method
 * testStaticMethod; only one of abstract, default, or static permitted
 * 
 * @param i
 * @return
 */
// public static abstract int testStaticMethod(float i);

default int testNonStaticMethod() {
    return 1;
}

/**
 * Without implementation.
 * 
 * @param i
 * @return
 */
int testNonStaticMethod(float i);

}

Kumar Abhishek
źródło
0

Niedozwolone połączenie modyfikatorów: statyczne i abstrakcyjne

Jeśli element członkowski klasy jest zadeklarowany jako statyczny, może być używany z nazwą klasy, która jest ograniczona do tej klasy, bez tworzenia obiektu.

Jeśli element członkowski klasy jest zadeklarowany jako abstrakcyjny, musisz zadeklarować klasę jako abstrakcyjną i musisz zapewnić implementację elementu abstrakcyjnego w jego odziedziczonej klasie (podklasie).

Musisz dostarczyć implementację do abstrakcyjnego elementu klasy w podklasie, w której zamierzasz zmienić zachowanie metody statycznej, również zadeklarowanej jako abstrakcyjna, która jest ograniczona do klasy bazowej, co nie jest poprawne

Sankar
źródło
Jak to odpowiada na pytanie? OP zapytał o interfejs, kiedy pisałeś o klasie.
Lucky
0

Ponieważ metody statyczne nie mogą być dziedziczone. Więc nie ma sensu umieszczać go w interfejsie. Interfejs jest w zasadzie umową, której muszą przestrzegać wszyscy jego abonenci. Umieszczenie statycznej metody w interfejsie zmusi subskrybentów do jej wdrożenia. co teraz staje się sprzeczne z faktem, że metody statyczne nie mogą być dziedziczone.

ip_x
źródło
metody statyczne są zawsze dziedziczone, ale nie można ich zastąpić.
Akki
0

W Javie 8 interfejsy mogą mieć teraz metody statyczne.

Na przykład komparator ma statyczną metodę naturalOrder ().

Zmniejszono również wymaganie, aby interfejsy nie mogły mieć implementacji. Interfejsy mogą teraz deklarować „domyślne” implementacje metod, które są jak zwykłe implementacje z jednym wyjątkiem: jeśli odziedziczysz zarówno domyślną implementację z interfejsu, jak i normalną implementację z nadklasy, implementacja nadklasy zawsze będzie miała priorytet.

Ishara
źródło
O MÓJ BOŻE! Mamy poważnego programistę (i mnie, komentatora) odpowiadającego (i ja komentującego) na pytanie sprzed 10 lat.
Mohamed Anees A,
Nie zauważyłem daty :)
Ishara
Ha ha! Nie ma problemu!
Mohamed Anees A,
-2

Być może przykładowy kod byłby pomocny, zamierzam użyć C #, ale powinieneś być w stanie to zrobić.

Załóżmy, że mamy interfejs o nazwie IPayable

public interface IPayable
{
    public Pay(double amount);
}

Teraz mamy dwie konkretne klasy, które implementują ten interfejs:

public class BusinessAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

public class CustomerAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

Teraz udawajmy, że mamy zbiór różnych kont, w tym celu użyjemy ogólnej listy typu IPayable

List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());

Teraz chcemy zapłacić 50,00 $ na wszystkie te konta:

foreach (IPayable account in accountsToPay)
{
    account.Pay(50.00);
}

Więc teraz widzisz, jak interfejsy są niezwykle przydatne.

Są używane tylko w przypadku obiektów, dla których utworzono instancję. Nie na zajęciach statycznych.

Gdybyś miał ustawioną płatność statyczną, podczas przeglądania IPayable w accountToPay nie byłoby sposobu, aby dowiedzieć się, czy należy wywołać płatność na koncie BusinessAcount czy CustomerAccount.

FlySwat
źródło
To, że metody statyczne nie mają sensu w TYM przykładzie, nie oznacza, że ​​nie mają one sensu w ŻADNYM przykładzie. W Twoim przykładzie, gdyby interfejs IPayable miał statyczną metodę „IncrementPayables”, która śledziłaby liczbę dodanych zobowiązań, byłby to prawdziwy przypadek użycia. Oczywiście zawsze można użyć klasy abstrakcyjnej, ale nie o to chodziło. Twój przykład sam w sobie nie podważa statycznych metod w interfejsach.
Cruncher