Практическая сторона способности определять класс в интерфейсе в Java? - PullRequest
12 голосов
/ 27 февраля 2009

Какова практическая сторона возможности определения класса в интерфейсе в Java:

interface IFoo
{
    class Bar
    {
        void foobar ()
        {
            System.out.println("foobaring...");
        }
    }
}

Ответы [ 11 ]

18 голосов
/ 27 февраля 2009

Я могу подумать о другом использовании, чем связанное Эриком П: определение реализации интерфейса по умолчанию / без операции.

. / Алекс

interface IEmployee
{

    void workHard ();  
    void procrastinate ();

    class DefaultEmployee implements IEmployee 
    {
        void workHard () { procrastinate(); };
        void procrastinate () {};
    }

}

Еще один пример - реализация Null Object Pattern :

interface IFoo
{
    void doFoo();
    IFoo NULL_FOO = new NullFoo();

    final class NullFoo implements IFoo
    {
        public void doFoo () {};
        private NullFoo ()  {};
    }
}


...
IFoo foo = IFoo.NULL_FOO;
...
bar.addFooListener (foo);
...
8 голосов
/ 27 февраля 2009

Я думаю эта страница довольно хорошо объясняет один пример. Вы бы использовали его для тесной привязки определенного типа к интерфейсу.

Бесстыдно сорвал с вышеуказанной ссылки:

interface employee{
    class Role{
          public String rolename;
          public int roleId;
     }
    Role getRole();
    // other methods
}

В приведенном выше интерфейсе вы строго привязываете тип роли к интерфейсу сотрудника (employee.Role).

6 голосов
/ 27 февраля 2009

Одно использование (к лучшему или к худшему) могло бы стать решением проблемы того, что Java не поддерживает статические методы в интерфейсах.

interface Foo {
    int[] getData();

    class _ {
        static int sum(Foo foo) {
            int sum = 0;
            for(int i: foo.getData()) {
                sum += i;
            }
            return sum;
        }
    }
}

Тогда вы бы назвали это с:

int sum = Foo._.sum(myFoo);
5 голосов
/ 27 февраля 2009

Я могу без колебаний сказать, что я никогда этого не делал. Я не могу придумать причину, почему вы бы тоже. Классы, вложенные в классы? Конечно, много причин для этого. В этих случаях я склонен считать эти внутренние классы деталями реализации. Очевидно, что интерфейс не имеет деталей реализации.

3 голосов
/ 01 марта 2009

Одно из мест, где интенсивно используется эта идиома, - XMLBeans . Цель этого проекта - взять схему XML и сгенерировать набор классов Java, которые можно использовать в двух направлениях для работы с документами XML, соответствующими этой схеме. Таким образом, он позволяет вам анализировать XML на XML-компоненты или создавать XML-компоненты и выводить их в XML.

Как правило, большинство типов схем xml сопоставляются с интерфейсом Java. Этот интерфейс имеет внутри себя фабрику, которая используется для генерации экземпляров этого интерфейса в реализации по умолчанию:

public interface Foo extends XmlObject {
  public boolean getBar();
  public boolean isSetBar();
  public void setBar(boolean bar);

  public static final SchemaType type = ...

  public static final class Factory {
    public static Foo newInstance() {
      return (Foo)XmlBeans.getContextTypeLoader().newInstance(Foo.type, null);
    }

    // other factory and parsing methods
  }
} 

Когда я впервые столкнулся с этим, казалось неправильным связывать весь этот ганк реализации с определением интерфейса. Тем не менее, мне на самом деле это понравилось, поскольку оно позволяло определять все в терминах интерфейсов, но иметь единый способ получения экземпляров интерфейса (в отличие от наличия другого внешнего класса фабрики / сборщика).

Я подобрал его для классов, где это имело смысл (особенно для тех, где я имел большой контроль над интерфейсом / имплами), и нашел его довольно чистым.

2 голосов
/ 02 марта 2009

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

public interface Printable {
    void print();

    public static class Caller {
        public static void print(Object mightBePrintable) {
            if (mightBePrintable instanceof Printable) {
                ((Printable) mightBePrintable).print();
            }
        }
    }
}

Теперь вместо этого:

void genericPrintMethod(Object obj) {
    if (obj instanceof Printable) {
        ((Printable) obj).print();
    }
}

Вы можете написать:

void genericPrintMethod(Object obj) {
   Printable.Caller.print(obj);
}
2 голосов
/ 27 февраля 2009

Google Web Toolkit использует такие классы для привязки «обычного» интерфейса к интерфейсу асинхронного вызова:

public interface LoginService extends RemoteService {

    /**
     * Utility/Convenience class.
     * Use LoginService.App.getInstance() to access static instance of LoginServiceAsync
     */
    class App {

        public static synchronized LoginServiceAsync getInstance() {
            ...
        }
    }
}
2 голосов
/ 27 февраля 2009

Полагаю, вы могли бы определить класс, который используется в качестве возвращаемого типа или типа параметра для методов в интерфейсе. Не кажется особенно полезным. С тем же успехом вы можете просто определить класс отдельно. Единственное возможное преимущество заключается в том, что он в некотором смысле объявляет класс «принадлежащим» интерфейсу.

1 голос
/ 01 марта 2009

Этот подход может использоваться для определения множества классов в одном файле. Это хорошо работало для меня в прошлом, когда у меня было много простых реализаций интерфейса. Однако, если бы я сделал это снова, я бы использовал enum, который реализует интерфейс, который был бы более элегантным решением.

1 голос
/ 27 февраля 2009

Я призываю к осторожности всякий раз, когда кажется хорошей идеей создать не приватный вложенный класс. Вам почти наверняка лучше пойти прямо на внешний класс. Но если вы собираетесь создать публичный вложенный класс, не кажется более странным поместить его в интерфейс, чем класс. Абстрактность внешнего класса не обязательно связана с абстрактностью вложенного класса.

...