Внутрішній клас в інтерфейсі


97

Чи можна створити внутрішній клас в інтерфейсі?
Якщо це можливо, чому ми хочемо створити такий внутрішній клас , оскільки ми не збираємося створювати будь-які об'єкти інтерфейсу?

Чи допомагають ці внутрішні класи в будь-якому процесі розвитку?

Відповіді:


51

Так, ви можете створити як вкладений клас, так і внутрішній клас всередині інтерфейсу Java (зауважте, що, на відміну від поширеної думки, не існує такого поняття як " статичний внутрішній клас ": це просто не має сенсу, немає нічого "внутрішнього" та " outter "клас, коли вкладений клас є статичним, тому він не може бути" статичним внутрішнім ").

У будь-якому випадку, наступне компілюється чудово:

public interface A {
    class B {
    }
}

Я бачив, як раніше застосовували якусь «перевірку контрактів» безпосередньо у визначенні інтерфейсу (ну, у класі, вкладеному в інтерфейс, який може мати статичні методи, на відміну від самого інтерфейсу, що не може). Виглядаючи так, якщо я правильно згадую.

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)
        }
    }
}

Зауважте, що я не коментую корисність такої речі, я просто відповідаю на ваше запитання: це можна зробити, і це один із видів використання, який я вже бачив із цього.

Тепер я не буду коментувати корисність такої конструкції, і з того, що я бачив: я бачив це, але це не дуже поширена конструкція.

Кодова база 200KLOC тут, де це трапляється рівно нульовий раз (але тоді ми маємо багато інших речей, які ми вважаємо поганими практиками, які трапляються рівно нульовим часом, що інші люди вважали б цілком нормальними, тому ...).


Чи можете ви додати кілька прикладів використання? Я тестував щось подібне деякий час тому і не зрозумів, що можна отримати від використання цієї конструкції.
Роман

@Roman: ну, я пам’ятаю, я стикався з цим у якомусь проекті (додав би відносно чистий проект, але вони не були моїми), але я не знаю, чи справді він чистий чи ні. Я додав крихітний приклад, схожий на те, що бачив, але ще раз: це не був мій код, і я не використовую цю конструкцію, тому я не найкваліфікованіший, щоб придумати дійсні приклади :) IIRC клас всередині завжди називався, наприклад StateChecker, а дзвінки завжди мали б виглядати: A.StateChecker.check (a) або щось подібне.
SyntaxT3rr0r

8
Якщо ви скажете, що "не існує такого поняття, як" статичний внутрішній клас ", ваша відповідь, що" ви можете створити як вкладений клас, так і внутрішній клас всередині інтерфейсу Java ", є принципово неправильною. Використовуючи ваше звужене визначення, interfaces не може мати внутрішні класи. Ви можете опустити staticмодифікатор interfaceвкладеного класу, але все-таки це вкладений клас, а не внутрішній клас.
Холгер

6
Ця відповідь неправильна. Інтерфейси можуть мати статичні вкладені класи, але не внутрішні класи.
Пол Бодінгтон

1
@PaulBoddington Ви маєте рацію. Навіть видаляючи 'static', Bклас є статичним вкладеним класом, а не внутрішнім класом; інтерфейси отримують особливу обробку. Я не зміг знайти згадки про це в Інтернеті, за винятком самої специфікації: "Клас-член інтерфейсу є неявно статичним, тому ніколи не вважається внутрішнім класом". docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Макс

109

Так, ми можемо мати класи всередині інтерфейсів. Одним із прикладів використання може бути

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();
}

Тут код має два вкладені класи, які призначені для інкапсуляції інформації про об'єкти подій, які згодом використовуються у визначеннях методів, таких як getKeyEvents (). Присутність їх у вхідному інтерфейсі покращує згуртованість.


3
@Levit Цікаво, як буде виглядати реалізований клас?
переобмін

1
Хотілося б побачити реалізацію в дії для вищезазначеного використання. Дякую.
Пракаш К

45

Дійсне використання IMHO - це визначення об'єктів, які отримуються або повертаються методами інтерфейсу, що додається. Типово структури зберігання даних. Таким чином, якщо об'єкт використовується тільки для цього інтерфейсу, у вас є речі більш згуртовані.

На прикладі:

interface UserChecker {
   Ticket validateUser(Credentials credentials);

   class Credentials {
      // user and password
   }

   class Ticket {
      // some obscure implementation
   }
}

Але як би там не було ... це лише справа смаку.


35

Цитата з специфікації Java 7 :

Інтерфейси можуть містити декларації типу членів (§8.5).

Оголошення типу учасника в інтерфейсі є неявно статичним і загальнодоступним. Дозволено надмірно вказати один або обидва ці модифікатори.

НЕ можна оголошувати нестатичні класи всередині інтерфейсу Java, що для мене є сенсом.


Дякую. Це, мабуть, найбільш стисла відповідь із усіх.
Джозеф

1
Це відповідь, яку я шукав .. але ОП задає кілька запитань.
Чарлі Уоллес,

11

Цікавим випадком використання є надання типу реалізації за замовчуванням для інтерфейсу методів через внутрішній клас, як описано тут: https://stackoverflow.com/a/3442218/454667 (для подолання проблеми успадкування одного класу).


І саме тому приватні класи для членів мали б сенс.
Вінсент

7

Це, безумовно, можливо, і один випадок, коли мені здається корисним, це коли інтерфейс повинен викидати власні винятки. Ви зберігаєте винятки із пов’язаним із ними інтерфейсом, який, на мою думку, часто акуратніше, ніж засмічення вашого вихідного дерева купою дрібницьких файлів виключень.

interface MyInterface {

   public static class MyInterfaceException extends Exception {
   }

   void doSomething() throws MyInterfaceException;
}

7

Так, можна мати визначення статичних класів всередині інтерфейсу, але, мабуть, найбільш корисним аспектом цієї функції є використання типів перечислення (які є особливим видом статичних класів). Наприклад, ви можете мати щось подібне:

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);
    ...
}


1

Можливо, коли вам потрібні більш складні конструкції, такі як різні способи реалізації, розгляньте:

public interface A {
    public void foo();

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

Це ваш інтерфейс, і це буде реалізованим:

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();
    }
}

Може надати деякі статичні реалізації, але чи не буде це бентежити, я не знаю.


0

Я знайшов використання ялиці цього типу конструкції.

  1. Ви можете використовувати цю конструкцію для визначення та групування всіх статичних кінцевих констант.
  2. Оскільки, це інтерфейс, який можна реалізувати на класі.

Ви маєте доступ до всіх згрупованих констант; ім'я класу в цьому випадку виконує роль простору імен.


0

Ви також можете створити статичні класи "Helper" для загальної функціональності для об'єктів, що реалізують цей інтерфейс:

public interface A {
    static class Helper {
        public static void commonlyUsedMethod( A a ) {
           ...
        }
    }
}

0

Зараз мені це потрібно. У мене є інтерфейс, де було б зручно повернути унікальний клас з декількох його методів. Цей клас має сенс лише як контейнер для відповідей методів цього інтерфейсу.

Отже, було б зручно мати статичне визначення вкладеного класу, яке пов'язане лише з цим інтерфейсом, оскільки цей інтерфейс повинен бути єдиним місцем, де коли-небудь створюється цей клас контейнера результатів.


0

Наприклад, риси (щось на зразок інтерфейсу з реалізованими методами) у Groovy. Вони компілюються в інтерфейс, який містить внутрішній клас, де реалізовані всі методи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.