Klasa wewnętrzna w interfejsie

98

Czy możliwe jest utworzenie klasy wewnętrznej w interfejsie?
Jeśli to możliwe, po co mielibyśmy tworzyć taką klasę wewnętrzną , skoro nie zamierzamy tworzyć żadnych obiektów interfejsu?

Czy te klasy wewnętrzne pomagają w jakimkolwiek procesie rozwoju?

gmhk
źródło

Odpowiedzi:

51

Tak, można tworzyć zarówno zagnieżdżone klasy lub wewnętrzną klasę wewnątrz interfejsu Java (Zauważ, że w przeciwieństwie do popularnego przekonania, że nie ma czegoś takiego jak " statyczne klasy wewnętrzna «: To po prostu nie ma sensu, nie ma nic «wewnętrzny» i nie» outter ”, gdy klasa zagnieżdżona jest statyczna, więc nie może być„ statyczną wewnętrzną ”).

W każdym razie poniższe kompilacje dobrze się komponują:

public interface A {
    class B {
    }
}

Widziałem, że wstawiał jakiś rodzaj "sprawdzania kontraktu" bezpośrednio w definicji interfejsu (cóż, w klasie zagnieżdżonej w interfejsie, która może mieć statyczne metody, w przeciwieństwie do samego interfejsu, który nie może). Wyglądam tak, jeśli dobrze pamiętam.

public interface A {
    static class B {
        public static boolean verifyState( A a ) {
            return (true if object implementing class A looks to be in a valid state)
        }
    }
}

Zwróć uwagę, że nie komentuję użyteczności takiej rzeczy, po prostu odpowiadam na twoje pytanie: można to zrobić i jest to jedyny rodzaj wykorzystania, jaki widziałem.

Teraz nie będę komentował przydatności takiego konstruktu i od tego, co widziałem: widziałem go, ale nie jest to zbyt powszechna konstrukcja.

Baza kodów 200KLOC tutaj, gdzie dzieje się to dokładnie zero razy (ale mamy wiele innych rzeczy, które uważamy za złe praktyki, które zdarzają się dokładnie zero razy, a inni ludzie uznaliby to za całkowicie normalne, więc ...).

Składnia T3rr0r
źródło
Czy możesz dodać przykłady użycia? Testowałem coś podobnego jakiś czas temu i nie rozumiałem, co mogę zyskać stosując tę ​​konstrukcję.
Roman
@Roman: cóż, pamiętam, że spotkałem się z tym w jakimś projekcie (dodałbym stosunkowo czysty projekt, ale nie były moje), ale nie wiem, czy jest naprawdę czysty, czy nie. Dodałem mały przykład, który wygląda jak ten, który widziałem, ale jeszcze raz: to nie był mój kod i nie używam tej konstrukcji, więc nie jestem najbardziej wykwalifikowany, aby wymyślić prawidłowe przykłady :) IIRC klasa wewnątrz była zawsze nazywana, na przykład StateChecker, a wywołania zawsze wyglądałyby następująco: A.StateChecker.check (a) lub coś w tym rodzaju.
SkładniaT3rr0r
8
Jeśli twierdzisz, że „nie ma czegoś takiego jak« statyczne klasy wewnętrznej »” odpowiedź, że « może tworzyć zarówno zagnieżdżone klasy lub wewnętrzną klasę wewnątrz interfejsu Java» jest z gruntu błędne. Używając twojej zawężonej definicji, interfaces nie mogą mieć klas wewnętrznych. Możesz pominąć staticmodyfikator interfaceklasy zagnieżdżonej, ale nadal jest to klasa zagnieżdżona, a nie klasa wewnętrzna.
Holger
6
Ta odpowiedź jest błędna. Interfejsy mogą mieć statyczne klasy zagnieżdżone, ale nie klasy wewnętrzne.
Paul Boddington,
1
@PaulBoddington Masz rację. Nawet po usunięciu „statycznej” Bklasa jest statyczną klasą zagnieżdżoną, a nie klasą wewnętrzną; interfejsy są traktowane w specjalny sposób. Nie mogłem znaleźć wzmianki o tym w Internecie, z wyjątkiem samej specyfikacji: „Klasa składowa interfejsu jest niejawnie statyczna, więc nigdy nie jest uważana za klasę wewnętrzną”. docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Max Barraclough
110

Tak, możemy mieć klasy wewnątrz interfejsów. Jednym z przykładów użycia może być

public interface Input
{
    public static class KeyEvent {
         public static final int KEY_DOWN = 0;
         public static final int KEY_UP = 1;
         public int type;
         public int keyCode;
         public char keyChar;
    }
    public static class TouchEvent {
         public static final int TOUCH_DOWN = 0;
         public static final int TOUCH_UP = 1;
         public static final int TOUCH_DRAGGED = 2;
         public int type;
         public int x, y;
         public int pointer;
    }
    public boolean isKeyPressed(int keyCode);
    public boolean isTouchDown(int pointer);
    public int getTouchX(int pointer);
    public int getTouchY(int pointer);
    public float getAccelX();
    public float getAccelY();
    public float getAccelZ();
    public List<KeyEvent> getKeyEvents();
    public List<TouchEvent> getTouchEvents();
}

Tutaj kod ma dwie zagnieżdżone klasy, które służą do hermetyzacji informacji o obiektach zdarzeń, które są później używane w definicjach metod, takich jak getKeyEvents (). Umieszczenie ich wewnątrz interfejsu wejściowego poprawia spójność.

zafar142003
źródło
3
@Levit Zastanawiasz się tylko, jak będzie wyglądać zaimplementowana klasa?
przewyższenie
1
Chciałbym zobaczyć implementację w akcji dla powyższego zastosowania. Dziękuję Ci.
Prakash K
46

Prawidłowym zastosowaniem, IMHO, jest definiowanie obiektów, które są odbierane lub zwracane przez otaczające metody interfejsu. Typowo struktury przechowujące dane. W ten sposób, jeśli obiekt jest używany tylko dla tego interfejsu, masz rzeczy w bardziej spójny sposób.

Przez przykład:

interface UserChecker {
   Ticket validateUser(Credentials credentials);

   class Credentials {
      // user and password
   }

   class Ticket {
      // some obscure implementation
   }
}

Ale w każdym razie ... to tylko kwestia gustu.

helios
źródło
36

Cytat ze specyfikacji Java 7 :

Interfejsy mogą zawierać deklaracje typu elementu członkowskiego (§ 8.5).

Deklaracja typu elementu członkowskiego w interfejsie jest niejawnie statyczna i publiczna. Dozwolone jest redundantne określanie jednego lub obu tych modyfikatorów.

NIE jest możliwe deklarowanie niestatycznych klas wewnątrz interfejsu Java, co dla mnie ma sens.

Nad Nik
źródło
Dziękuję Ci. To prawdopodobnie najbardziej zwięzła odpowiedź ze wszystkich.
Józef
1
To jest odpowiedź, której szukałem… ale PO zadaje kilka pytań… tak czy inaczej mam aktualizację.
Charlie Wallace
11

Ciekawym przypadkiem użycia jest zapewnienie pewnego rodzaju domyślnej implementacji metod interfejsu poprzez klasę wewnętrzną, jak opisano tutaj: https://stackoverflow.com/a/3442218/454667 (aby przezwyciężyć problem dziedziczenia pojedynczej klasy).

Bachi
źródło
I właśnie dlatego prywatne klasy członkowskie miałyby sens.
Vincent
7

Z pewnością jest to możliwe, a jednym z przypadków, w których uznałem to za przydatne, jest sytuacja, gdy interfejs musi wyrzucać niestandardowe wyjątki. Zachowujesz wyjątki wraz z powiązanym z nimi interfejsem, co, jak sądzę, jest często lepsze niż zaśmiecanie drzewa źródłowego stosami trywialnych plików wyjątków.

interface MyInterface {

   public static class MyInterfaceException extends Exception {
   }

   void doSomething() throws MyInterfaceException;
}
Michael Anderson
źródło
7

Tak, możliwe jest posiadanie statycznych definicji klas wewnątrz interfejsu, ale być może najbardziej użytecznym aspektem tej funkcji jest użycie typów wyliczeniowych (które są specjalnym rodzajem klas statycznych). Na przykład możesz mieć coś takiego:

public interface User {
    public enum Role {
        ADMIN("administrator"),
        EDITOR("editor"),
        VANILLA("regular user");

        private String description;

        private Role(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }
    }

    public String getName();
    public void setName(String name);
    public Role getRole();
    public void setRole(Role role);
    ...
}
raspacorp
źródło
1

To, o czym wspomina @Bachi, jest podobne do cech w Scali i są w rzeczywistości implementowane za pomocą zagnieżdżonej klasy wewnątrz interfejsu. Można to zasymulować w Javie. Zobacz także cechy java lub wzorzec mieszanki?

Henno Vermeulen
źródło
1

Może jeśli potrzebujesz bardziej złożonych konstrukcji, takich jak różne zachowania implementacyjne, rozważ:

public interface A {
    public void foo();

    public static class B implements A {
        @Override
        public void foo() {
            System.out.println("B foo");
        }
    }
}

To jest twój interfejs, a to będzie implementowany:

public class C implements A {
    @Override
    public void foo() {
        A.B b = new A.B();
        b.foo(); 
    }

    public static void main(String[] strings) {
        C c = new C();
        c.foo();
    }
}

Może zawierać statyczne implementacje, ale nie będzie to mylące, nie wiem.

Ilian Zapryanov
źródło
0

Znalazłem zastosowanie dla tego typu konstrukcji.

  1. Możesz użyć tej konstrukcji do zdefiniowania i zgrupowania wszystkich statycznych stałych końcowych.
  2. Ponieważ jest to interfejs, możesz to zaimplementować w klasie.

Masz dostęp do wszystkich zgrupowanych stałych; nazwa klasy działa w tym przypadku jako przestrzeń nazw.

Venkat K.
źródło
0

Możesz także utworzyć statyczne klasy „Pomocnicze” dla typowych funkcji dla obiektów, które implementują ten interfejs:

public interface A {
    static class Helper {
        public static void commonlyUsedMethod( A a ) {
           ...
        }
    }
}
Zwycięzca
źródło
0

Potrzebuję go teraz. Mam interfejs, w którym wygodnie byłoby zwrócić unikalną klasę z kilku jej metod. Ta klasa ma sens tylko jako kontener dla odpowiedzi z metod tego interfejsu.

Dlatego wygodnie byłoby mieć statyczną definicję klasy zagnieżdżonej, która jest skojarzona tylko z tym interfejsem, ponieważ ten interfejs powinien być jedynym miejscem, w którym ta klasa kontenera wyników jest kiedykolwiek tworzona.

Panie Co
źródło
0

Na przykład cechy (coś w rodzaju interfejsu z zaimplementowanymi metodami) w Groovy. Są kompilowane do interfejsu, który zawiera wewnętrzną klasę, w której zaimplementowane są wszystkie metody.

dehasi
źródło