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

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

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

Ответов (11)

Решение

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

./Алекс

interface IEmployee
{

    void workHard ();  
    void procrastinate ();

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

}

Еще один пример - реализация шаблона нулевого объекта :

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

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

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

Кажется, что это повсюду написано «Плохое дизайнерское решение».

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

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

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

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

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

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() {
            ...
        }
    }
}

Одно использование (к лучшему или худшему) было бы обходным путем для того факта, что 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);

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

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

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

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

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

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

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

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