Я пытался использовать процессоры аннотаций для создания реализаций конкретных интерфейсов Factory. Эти интерфейсы выглядят следующим образом:
public interface ViewFactory<T extends View> {
<S extends Presenter<T>> T create(S presenter);
}
и
public interface PresenterFactory<T extends View> {
<S extends Presenter<T>> S create();
}
Обработчик аннотаций делает правильные вещи и генерирует фабрику для каждого соответствующего класса, которая аннотируется соответствующей аннотацией.
Результат обработки аннотаций следующий:
public final class TestViewImplFactory implements ViewFactory {
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
и соответствующий другой класс:
public final class TestPresenterImplFactory implements PresenterFactory {
public final TestPresenter create() {
return new TestPresenterImpl();
}
}
Однако TestViewImplFactory не может быть скомпилирован. Сообщение об ошибке:
"Class 'TestViewImplFactory' must be declared abstract or implement abstract method create(S) in 'ViewFactory'"
Java говорит, что верно следующее:
@Override
public View create(Presenter presenter) {
return new TestViewImpl(presenter);
}
что вообще не сработает, учитывая, что пользователь хочет знать, какой View будет возвращен и какой Presenter требуется. Я ожидал, что:
потому что они оба действительно похожи. Я ожидал, что первое окажется правдой.
Что мне здесь не хватает?
Если я добавлю Generic тип в TestViewImplFactory следующим образом:
public final class TestViewImplFactory implements ViewFactory<TestView> {
@Override
public <S extends Presenter<TestView>> TestView create(S presenter) {
return new TestViewImpl(presenter);
}
}
Возникает проблема, что параметр конструктора (который имеет тип TestPresenter) неверен. Изменение S на конкретный TestPresenter снова сделает класс не компилируемым по той же причине, что и выше.
Итак, я наткнулся на «решение», которое можно скомпилировать.
Что в основном нужно сделать, так это изменить интерфейс ViewFactory на следующее:
public interface ViewFactory<T extends View, S extends Presenter<T>> {
T create(S presenter);
}
Таким образом, определение класса имеет тот же универсальный тип, что и метод в вопросе выше.
После компиляции (на этот раз со спецификацией универсального типа) вывод выглядит следующим образом:
public final class TestViewImplFactory implements ViewFactory<TestView, TestPresenter> {
public TestViewImplFactory() {
}
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
Это может быть скомпилировано и успешно работает.
Однако это не отвечает на исходный вопрос. Почему универсальный тип явно указан в определении типа правильно, но унаследованный и, указанный в объявлении метода, неверен и не компилируется?
Чтобы быть конкретным: почему Java может наследовать один Generic автоматически (в PresenterFactory), а другие нет (в ViewFactory, в методе и в объявлении типа)?
@killjoy Или, что более вероятно, неспособность отразить параметры типа в классах, которые он обрабатывает, и просто использовать стертые подписи.
@killjoy Это не должно повлиять на результат. Если это будет проблемой, TestPresenterImplFactory также не будет компилироваться. Или я здесь не прав?
Если вы используете стертые типы, вам необходимо последовательно использовать их, брать в качестве аргумента только тип стирания и возвращать соответствующее стирание. Вы говорите, что пользователю потребуются конкретные (общие) типы, но на самом деле они ему не понадобятся, если они будут взаимодействовать только с общим интерфейсом, а не с сгенерированным типом реализации. Компилятор вставит правильные приведения.




Почему не работает:
public interface PresenterFactory<T extends View> {
<S extends Presenter<T>> S create();
}
Эта подпись заставляет компилятор определять S в том месте, где вызывается create(). S будет тем, что вы назначили create(), например:
FancyPresenter fp = presenterFactory.create();
SomeOtherPresenter sop = presenterFactory.create();
Это означает, что:
public TestPresenter create(){...}
не является реализацией:
<S extends Presenter<T>> S create();
но метод переопределения. Реализации метода интерфейса нет. Невозможно даже реализовать какую-либо реализацию с конкретным S. Это похоже на:
public interface ViewFactory<T extends View> {
<S extends Presenter<T>> T create(S presenter);
}
Здесь универсальный снова выводится при вызове метода. Таким образом, реализация должна принимать каждый подтип Presenter<T>. Единственная допустимая реализация для этого:
public interface ViewFactory<T extends View> {
T create(Presenter<T> presenter);
}
Но тип возврата зависит от параметра presenter. Это может сработать, если presenter предоставляет вам метод только для создания экземпляра T.
Почему работает другое решение:
Связывание универсального метода через тип означает, что реализация интерфейса предоставляет конкретный тип. Таким образом, для одного объекта вам не нужно предоставлять несколько разных привязок. Независимо от того, где вы вызываете метод create() для PresenterFactory<TestView, TestPresenter<TestView>>, универсальный тип возвращаемого значения привязан к TestPresenter<TestView>. Таким образом, существует возможная реализация для каждого подтипа PresenterFactory<...>.
Я думаю, что следует рассмотреть самую первую часть вашей постановки проблемы, поскольку я заметил, что ваш процессор аннотаций реализует необработанный тип ViewFactory. Я думаю, что со стиранием типа, поскольку это сгенерированный код, на практике это не имеет большого значения. Но если бы процессор мог генерировать реализации, используя параметризованный тип, было бы, по крайней мере, легче рассуждать о проблеме.
Итак, учитывая подпись сообщения <S extends Presenter<T>> T create(S presenter), вы могли бы сгенерировать:
public class TestViewImplFactory implements ViewFactory<TestView> {
@Override
public <S extends Presenter<TestView>> TestView create(S presenter) { ... }
}
Или более минимально:
public class TestViewImplFactory implements ViewFactory<TestView> {
@Override
public TestView create(Presenter presenter) { ... }
}
Но тогда, с любым из них, вы не можете ограничить параметр до TestPresenter. Вам нужно будет заменить ViewFactory на что-то вроде
public interface ViewFactory<T extends View, U extends Presenter<T>>
и они реализуют ViewFactory<TestView, TestPresenter>. Вы как бы должны использовать параметры типа в реализации, чтобы достичь желаемых ограничений типа.
ваш обработчик аннотаций, похоже, создает необработанные универсальные типы.