Возьмите аргумент, который реализует несколько интерфейсов для создания объекта - PullRequest
0 голосов
/ 27 февраля 2011

У меня есть объект, который я хочу создать, используя объект, который реализует два интерфейса (предположим, я не могу изменить класс объекта для создания третьего интерфейса, который будет расширять два интерфейса).Как лучше всего создавать такие объекты с помощью guice?

Ответы [ 3 ]

4 голосов
/ 28 февраля 2011

Java позволяет вам создавать комбинации интерфейсов, то есть

static class TL<T extends IA & IB> extends TypeLiteral<T>(){}

, но вы заметите, что действительно должен быть конкретный тип T, который реализует оба этих интерфейса.Java не может придумать «комбинированные типы» для переменных - тип должен существовать с реальным файлом класса.

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

Что следует заметить по поводу кода ниже:

  • Яфактически злоупотребляя встроенными мерами безопасности Guice, делая подкласс TL TypeLiteral параметризованным на IA вместо TypeLiteral на T, что было бы правильно.Так что на самом деле весь этот пример, вероятно, работает случайно.
  • Это не сильно помогает.Что-то, где-то, наконец, нужно указать конкретный тип (класс Both) в этом случае.Это позволяет вам задавать конкретный тип в классах, которые используют объект, но не тогда, когда вы связываете его или запрашиваете его непосредственно из инжектора
  • Почти никто не понимает TypeA &Синтаксис TypeB с обобщениями Java - подготовьтесь к тому, что любой, кто смотрит на этот код, будет полностью сбит с толку, даже некоторые гуру Java
  • Если вы позже попытаетесь использовать объект, который реализует только один интерфейсовВы могли бы создать динамический прокси-сервер, который реализует один интерфейс и делегирует объекту, но вам все равно потребуется создать интерфейс, который объединяет эти два параметра, чтобы дать Java тип для ссылки на

публичный класс X {

static final class TL<T extends IA & IB> extends TypeLiteral<IA> {}

interface IA {}
interface IB {}
static final class Both implements IA, IB {}

@Test
public void test() {
    Injector inj = Guice.createInjector(new M());
    Both object = inj.getInstance(Key.get(new TypeLiteral<Both>(){}));
    assertNotNull(object);
    Foo<Both> foo = inj.getInstance(Key.get(new TypeLiteral<Foo<Both>>() {}));
    assertTrue (object instanceof IA);
    assertTrue (object instanceof IB);
    assertNotNull(foo);
    assertNotNull(foo.obj);
}

static class Foo<T extends IA & IB> {
    private final T obj;
    @Inject
    Foo(T obj) {
        this.obj = obj;
    }
}

static class M extends AbstractModule {
    @Override
    protected void configure() {
        bind(new TL<Both>()).to(Both.class);
    }
}
}

Так что я думаю, что ответ, вы можете, но, вероятно, не должны.

3 голосов
/ 27 февраля 2011

Либо внедрите один интерфейс и приведите к другому, когда это необходимо, либо введите один и тот же объект дважды, как два разных интерфейса.

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

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

Почему бы просто не сделать:

// Your original class
class AB implements IA, IB {...}

// In a Module
bind(AB.class).in(SOMESCOPE);
bind(IA.class).to(AB.class);
bind(IB.class).to(AB.class);

// In the object to be inejcted with AB
class MyClass {
    @Inject IA a;
    @Inject IB b;
}

В конце концов, у вас на самом деле будет a == b, разве это не будет соответствовать требованиям?

...