Dlaczego w Javie miałby być używany statycznie zagnieżdżony interfejs?

235

Właśnie znalazłem statyczny interfejs zagnieżdżony w naszej bazie kodu.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Nigdy wcześniej tego nie widziałem. Pierwotny programista jest poza zasięgiem. Dlatego muszę zapytać SO:

Jakie są semantyki za statycznym interfejsem? Co by się zmieniło, jeśli usunę static? Dlaczego ktoś miałby to robić?

Mo.
źródło
2
To nie jest „interfejs wewnętrzny: to interfejs zagnieżdżony . Wewnętrzny ma określone znaczenie w Javie.
Markiz Lorne

Odpowiedzi:

293

Słowo kluczowe static w powyższym przykładzie jest redundantne (interfejs zagnieżdżony jest automatycznie „statyczny”) i można je usunąć bez wpływu na semantykę; Polecam go usunąć. To samo dotyczy „publicznych” metod interfejsu i „publicznego finału” w polach interfejsu - modyfikatory są redundantne i po prostu dodają bałaganu do kodu źródłowego.

Tak czy inaczej, programista po prostu deklaruje interfejs o nazwie Foo.Bar. Nie ma dalszego powiązania z klasą obejmującą, z wyjątkiem tego, że kod, który nie może uzyskać dostępu do Foo, nie będzie mógł uzyskać dostępu do Foo.Bar. (Z kodu źródłowego - kod bajtowy lub odbicie może uzyskać dostęp do Foo.Bar, nawet jeśli Foo jest prywatny dla pakietu!)

Akceptowalnym stylem jest tworzenie zagnieżdżonego interfejsu w ten sposób, jeśli spodziewasz się, że będzie używany tylko z klasy zewnętrznej, aby nie tworzyć nowej nazwy najwyższego poziomu. Na przykład:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Jesse Glick
źródło
1
W odpowiedzi Jesse Glick, co to oznacza: (Z kodu źródłowego - kod bajtowy lub odbicie może uzyskać dostęp do Foo.Bar, nawet jeśli Foo jest prywatny dla pakietu!).
Vasu,
Kaillash, do prywatnych metod można uzyskać dostęp przez relfection (w pakiecie odbicia) i poprzez bezpośredni dostęp do kodu bajtowego wygenerowanych plików .class.
gmoore
2
Przez „bytecode ... można uzyskać dostęp do Foo.Bar” Mam na myśli, że skompilowana klasa odwołująca się do Foo.Bar może zostać załadowana i uruchomiona, nawet jeśli nie może odwoływać się do Foo.Bar. Może się to zdarzyć, jeśli klasa została skompilowana wcześniej, kiedy Foo była publiczna, lub jeśli klasa została ręcznie skompilowana lub skompilowana z jakiegoś języka innego niż Java itp. Kompilator Java sprawdza jednak modyfikator dostępu w klasie zamykającej nawet jeśli wynikowy kod bajtowy nie odwoływałby się do tej otaczającej klasy.
Jesse Glick
@Jesse Czy do prywatnej klasy statycznej w prywatnej klasie najwyższego poziomu można uzyskać dostęp poprzez odbicie?
Pacerier
1
@Pacerier: nie. Mówiąc dokładniej, można załadować klasę i sprawdzić jej elementy, ale nie można utworzyć jej instancji ani wywoływać metod bez użycia setAccessible (true). Innymi słowy, jest widoczny, ale niedostępny, poprzez odbicie. Ale jeśli zagnieżdżona klasa statyczna jest publiczna, jest ona domyślnie dostępna poprzez odbicie, nawet jeśli nie jest dostępna statycznie (podczas kompilacji).
Jesse Glick,
72

Odpowiedzi na pytanie, ale dobrym powodem do korzystania z zagnieżdżonego interfejsu jest to, że jego funkcja jest bezpośrednio związana z klasą, w której się znajduje. Dobrym tego przykładem jest Listener. Jeśli miałeś klasę Fooi chciałeś, aby inne klasy mogły nasłuchiwać zdarzeń na niej, możesz zadeklarować interfejs o nazwie FooListener, co jest w porządku, ale prawdopodobnie łatwiej byłoby zadeklarować interfejs zagnieżdżony i zaimplementować inne klasy Foo.Listener( klasa zagnieżdżona Foo.Eventnie jest zła).

ColinD
źródło
11
Typowym przykładem jest java.util.Map.Entry(który jest interfejsem zagnieżdżonym w innym interfejsie).
Paŭlo Ebermann
4
Wiem, że to stary temat, ale wolałbym, aby klasa zewnętrzna znajdowała się we własnym pakiecie, a wszelkie dodatkowe interfejsy (np. Map.Entry) Lub klasy również w tym pakiecie. Mówię to, ponieważ lubię, aby moje lekcje były krótkie i rzeczowe. Również czytelnik może zobaczyć, jakie inne byty są powiązane z klasą, patrząc na klasy w pakiecie. Prawdopodobnie miałbym pakiet java.collections.mapmap. Chodzi o OO i modułowość. java.utilma w tym za dużo. utiljest jak common- zapach IMO
David Kerr
1
@Shaggy: na java.utilpewno ma w tym za dużo. To powiedziawszy, nie sądzę, aby podział na tak drobnoziarniste pakiety, jak sugerujesz, byłby idealny.
ColinD
1
@DavidKerr Załóżmy, że wywołujesz ten interfejs java.util.MapEntrypoza jego własnym pakietem. Pierwszy refleks: jakie są interfejsy związane z tą klasą? Patrzę na klasę. O ile javadoc nie prowadzi do tego interfejsu, nie mam pojęcia o ich związku. Plus ludzie nie patrzą na pakiet importu. Każdy ma swoje opinie. Nikt nie ma racji, nikt się nie myli
Raymond Chenon
@RaymondChenon częścią tego, co powiedziałem, było umieszczenie Map i powiązanych z nim klas, np. Map.Entry w osobnym pakiecie, np. Java.collections.map. W ten sposób cały kod związany z mapą znajduje się w jednym miejscu (pakiecie), a klasa Map najwyższego poziomu nie jest obciążona. Może umieszczę powiązane implementacje (HashMap itp.) W tym samym pakiecie.
David Kerr,
14

Interfejsy członków są domyślnie statyczne. Modyfikator statyczny w twoim przykładzie można usunąć bez zmiany semantyki kodu. Zobacz także specyfikację języka Java 8.5.1. Statyczne deklaracje typu pręta

Bas Leijdekkers
źródło
To nie jest „interfejs wewnętrzny”: to interfejs zagnieżdżony. Wewnętrzny ma określone znaczenie w Javie.
Markiz Lorne
@EJP, czytam różne blogi, używając terminów zamiennie. Czy istnieje interfejs wewnętrzny? Czym różnią się od interfejsu zagnieżdżonego?
Number945
@BreakingBenjamin, Zagnieżdżony interfejs oznacza statyczny. Istnieje klasa wewnętrzna i klasa zagnieżdżona, ale nie ma interfejsu wewnętrznego. Nawet jeśli tak, należy go nazwać interfejsem zagnieżdżonym. Wewnętrzny - niestatyczny i zagnieżdżony jest statyczny.
Sundar Rajan
9

Wewnętrzny interfejs musi być statyczny, aby można było uzyskać do niego dostęp. Interfejs nie jest powiązany z instancjami klasy, ale z samą klasą, więc można uzyskać do niej dostęp w następujący Foo.Barsposób:

public class Baz implements Foo.Bar {
   ...
}

W większości przypadków nie różni się to od statycznej klasy wewnętrznej.

Clinton N. Dreisbach
źródło
35
Zagnieżdżony interfejs jest automatycznie statyczny, niezależnie od tego, czy zapisuje się słowo kluczowe, czy nie.
Paŭlo Ebermann
3
Naprawdę potrzebujemy sposobu, aby społeczność zagłosowała, aby zaakceptować inną odpowiedź: stackoverflow.com/a/74400/632951
Pacerier
@ ClintonN.Dreisbach Czy możesz wyjaśnić, co należy rozumieć The interface isn't associated with instances of the class, but with the class itselfdalej, nie rozumiem
Kasun Siyambalapitiya
6

Odpowiedź Jessego jest bliska, ale myślę, że istnieje lepszy kod, który pokazuje, dlaczego wewnętrzny interfejs może być przydatny. Zanim zaczniesz czytać, spójrz na poniższy kod. Czy możesz dowiedzieć się, dlaczego wewnętrzny interfejs jest użyteczny? Odpowiedź jest taka, że ​​klasa DoSomethingAlready może być utworzona za pomocą dowolnej klasy, która implementuje A i C; nie tylko zoo konkretnej klasy. Oczywiście można to osiągnąć, nawet jeśli AC nie jest wewnętrzny, ale wyobraź sobie łączenie dłuższych nazw (nie tylko A i C) i robienie tego dla innych kombinacji (powiedzmy, A i B, C i B itp.) I łatwo zobaczyć, jak sprawy wymykają się spod kontroli. Nie wspominając o tym, że osoby przeglądające twoje drzewo źródłowe zostaną przytłoczone interfejsami, które mają znaczenie tylko w jednej klasie. Podsumowując,wewnętrzny interfejs umożliwia konstruowanie niestandardowych typów i poprawia ich enkapsulację .

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
użytkownik1982892
źródło
2
To nonsens. Klasa Zoojest nie realizować interfejs ACzatem przypadki Zoonie mogą być przekazywane do konstruktora DoSomethingAlreadyktórych oczekuje AC. Fakt, który ACrozszerza oba, Ai Cnie oznacza, że ​​klasy implementują Ai Cmagicznie także implementują AC.
Holger
3

Aby odpowiedzieć na twoje pytanie bardzo bezpośrednio, spójrz na Map.Entry.

Map.Entry

może to również być przydatne

Wpis blogu statycznego zagnieżdżonego Inerfaces

Henry B.
źródło
4
To jest przykład, ale tak naprawdę nie odpowiedź.
Paŭlo Ebermann
Używam Map.Entry do tworzenia obiektów „Parujących”. Jest odsłonięty. Implementacja Pary ma dwie szkoły myślenia, ale nie o to tutaj chodzi. Map.Entry może być wewnętrzny, ale używam go na zewnątrz.
Ravindranath Akila
0

Zazwyczaj widzę statyczne klasy wewnętrzne. Statyczne klasy wewnętrzne nie mogą odwoływać się do klas zawierających, o ile mogą to robić klasy niestatyczne. Chyba, że ​​napotykasz jakieś kolizje pakietów (w tym samym pakiecie co Foo jest już interfejs o nazwie Bar). Myślę, że uczynię go własnym plikiem. Może to być również decyzja projektowa w celu wymuszenia logicznego połączenia między Foo a Barem. Być może autor chciał, aby Bar był używany tylko z Foo (chociaż statyczny interfejs wewnętrzny nie wymusza tego, tylko logiczne połączenie)

basszero
źródło
„Statyczny wewnętrzny” jest wewnętrznie sprzecznością. Klasy zagnieżdżone są zarówno statyczne lub wewnętrzna.
Markiz Lorne
0

Jeśli zmienisz klasę Foo w interfejs Foo, słowo kluczowe „public” w powyższym przykładzie również będzie zbędne, ponieważ

interfejs zdefiniowany w innym interfejsie będzie domyślnie statyczny.

Danyło Wołoch
źródło
Nie odpowiedziałem na pytanie
Pada
0

W 1998 r. Philip Wadler zasugerował różnicę między interfejsami statycznymi a interfejsami niestatycznymi.

O ile widzę, jedyną różnicą w uczynieniu interfejsu niestatycznym jest to, że może on teraz zawierać niestatyczne klasy wewnętrzne; więc zmiana nie spowodowałaby unieważnienia żadnych istniejących programów Java.

Na przykład zaproponował rozwiązanie problemu wyrażenia , które polega na niedopasowaniu wyrażenia jako „ile twój język może wyrazić” z jednej strony, a wyrażeniem jako „warunki, które próbujesz przedstawić w swoim języku” z drugiej strony .

Przykład różnicy między statycznymi i niestatycznymi zagnieżdżonymi interfejsami można zobaczyć w jego przykładowym kodzie :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Jego sugestia nigdy nie dotarła do Java 1.5.0. Dlatego wszystkie pozostałe odpowiedzi są poprawne: nie ma różnicy między statycznymi i niestatycznymi zagnieżdżonymi interfejsami.

Pindatjuh
źródło
Należy zauważyć, że wszystko to odnosi się do GJ, który był bardzo wczesnym procesorem dla generycznych w Javie.
Markiz Lorne
-1

W Javie interfejs / klasa statyczna umożliwia używanie interfejsu / klasy jak klasy najwyższego poziomu, tzn. Może być zadeklarowane przez inne klasy. Możesz więc:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Bez statycznego powyższego nie udałoby się skompilować. Zaletą tego jest to, że nie potrzebujesz nowego pliku źródłowego, aby zadeklarować interfejs. Również wizualnie kojarzy interfejs Bar z klasą Foo, ponieważ musisz napisać Foo.Bar i implikuje, że klasa Foo robi coś z instancjami Foo.Bar.

Opis typów klas w Javie .

Skizz
źródło
Bez statyczne to robi kompilacji. „Statyczny” jest zbędny.
Markiz Lorne
@EJP: Zgodnie z tym artykułem, do którego podłączyłem, bez statycznej klasy wewnętrznej może używać tylko klasa będąca właścicielem, a nie nic poza klasą należną. Statyczny sprawia, że ​​jest zagnieżdżoną klasą najwyższego poziomu i może być używany przez obiekty poza zakresem klasy właściciela (przynajmniej, zgodnie z artykułem). Statyczne nie jest całkowicie zbędne i wpływa na zakres klasy wewnętrznej. Dotyczy to klas, interfejsy mogą zawsze działać jak obiekty najwyższego poziomu (należy przeczytać specyfikację języka, aby sprawdzić). Być może powinienem był oddzielić część interfejsu i klasy mojej odpowiedzi (moje złe).
Skizz
-6

Statyczny oznacza, że ​​dowolna część klasy pakietu (projektu) może uzyskać do niego dostęp bez użycia wskaźnika. Może to być przydatne lub utrudniające w zależności od sytuacji.

Doskonałym przykładem przydatności metod „statycznych” jest klasa Math. Wszystkie metody matematyczne są statyczne. Oznacza to, że nie musisz zejść z drogi, stworzyć nowej instancji, zadeklarować zmienne i zapisać je w jeszcze większej liczbie zmiennych, możesz po prostu wprowadzić swoje dane i uzyskać wynik.

Statyczne nie zawsze jest tak przydatne. Jeśli na przykład porównujesz wielkość liter, możesz chcieć przechowywać dane na kilka różnych sposobów. Nie można utworzyć trzech metod statycznych z identycznymi podpisami. Potrzebujesz 3 różnych instancji, niestatycznych, a następnie możesz i porównaj, ponieważ jeśli jest statyczny, dane nie zmienią się wraz z danymi wejściowymi.

Metody statyczne są dobre dla jednorazowych zwrotów i szybkich obliczeń lub łatwych do uzyskania danych.

Vordreller
źródło
1
Wiem, co oznacza statyczny. Po prostu nigdy wcześniej nie widziałem tego na interfejsie. Dlatego twoje pytanie jest nie na temat - przepraszam
pon.
1
Myślę, że jego odpowiedź jest nie na temat.
Koray Tugay