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

97

Можно ли создать внутренний класс в интерфейсе?
Если это возможно, зачем нам создавать такой внутренний класс, если мы не собираемся создавать какие-либо объекты интерфейса?

Помогают ли эти внутренние классы в любом процессе разработки?

gmhk
источник

Ответы:

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 здесь, где это происходит точно в нулевое время (но тогда у нас есть много других вещей, которые мы считаем плохими практиками, которые тоже происходят точно в нулевое время, что другие люди сочли бы совершенно нормальным, так что ...).

Синтаксис T3rr0r
источник
Можете добавить несколько примеров использования? Некоторое время назад я тестировал нечто подобное и не понял, что я могу получить от использования этой конструкции.
Роман
@Roman: ну, я помню, что столкнулся с этим в каком-то проекте (я бы добавил относительно чистый проект, но они не были моими), но я не знаю, действительно ли он чистый или нет. Я добавил крошечный пример, похожий на то, что я видел, но еще раз: это был не мой код, и я не использую эту конструкцию, поэтому я не самый квалифицированный, чтобы придумывать действительные примеры :) IIRC класс внутри всегда имел имя, например StateChecker, и вызовы всегда выглядели так: A.StateChecker.check (a) или что-то в этом роде.
SyntaxT3rr0r 08
8
Если вы говорите, что «не существует такой вещи, как« статический внутренний класс »», ваш ответ, что «вы можете создать как вложенный класс, так и внутренний класс внутри интерфейса Java», в корне неверен. Используя ваше суженное определение, interfaces не может иметь внутренних классов. Вы можете опустить staticмодификатор interfaceвложенного класса, но все же это вложенный класс, а не внутренний класс.
Holger
6
Это неверный ответ. Интерфейсы могут иметь статические вложенные классы, но не внутренние классы.
Пол Боддингтон,
1
@PaulBoddington Ты прав. Даже если удалить «статический», Bкласс будет статическим вложенным классом, а не внутренним классом; Особый подход к интерфейсам. Я не смог найти упоминания об этом в Интернете, за исключением самой спецификации: «Класс-член интерфейса неявно статичен, поэтому никогда не считается внутренним классом». docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Макс Барраклаф,
110

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

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 (). Их наличие внутри интерфейса ввода улучшает согласованность.

zafar142003
источник
3
@Levit Просто интересно, как будет выглядеть реализованный класс?
overxchange
1
Хотелось бы увидеть реализацию в действии для вышеуказанного использования. Спасибо.
Prakash K
45

Допустимое использование, IMHO, - это определение объектов, которые принимаются или возвращаются включающими методами интерфейса. Обычно структуры хранения данных. Таким образом, если объект используется только для этого интерфейса, у вас все будет более связным.

По примеру:

interface UserChecker {
   Ticket validateUser(Credentials credentials);

   class Credentials {
      // user and password
   }

   class Ticket {
      // some obscure implementation
   }
}

Но все равно ... это дело вкуса.

helios
источник
35

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

Интерфейсы могут содержать объявления типов членов (§8.5).

Объявление типа члена в интерфейсе неявно статично и открыто. Допускается избыточное указание одного или обоих этих модификаторов.

НЕЛЬЗЯ объявлять нестатические классы внутри интерфейса Java, что для меня имеет смысл.

Над Ник
источник
Спасибо. Это, наверное, самый лаконичный ответ из всех.
Джозеф
1
Это ответ, который я искал ... но OP задает несколько вопросов ... в любом случае, у меня есть апдук.
Чарли Уоллес
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

То, что упоминает @Bachi, похоже на трейты в Scala и фактически реализовано с использованием вложенного класса внутри интерфейса. Это можно смоделировать на Java. Смотрите также особенности java или шаблон миксинов?

Хенно Вермёлен
источник
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

Вы также можете создать статические классы «Помощник» для общих функций для объектов, реализующих этот интерфейс:

public interface A {
    static class Helper {
        public static void commonlyUsedMethod( A a ) {
           ...
        }
    }
}
Виктор
источник
0

Мне он нужен прямо сейчас. У меня есть интерфейс, в котором было бы удобно возвращать уникальный класс из нескольких его методов. Этот класс имеет смысл только как контейнер для ответов от методов этого интерфейса.

Следовательно, было бы удобно иметь статическое определение вложенного класса, которое связано только с этим интерфейсом, поскольку этот интерфейс должен быть единственным местом, где когда-либо создается этот класс контейнера результатов.

Мистер Что
источник
0

Например, трейты (что-то вроде интерфейса с реализованными методами) в Groovy. Они компилируются в интерфейс, который содержит внутренний класс, в котором реализованы все методы.

дехаси
источник