Общие поля @Inject'd в абстрактном суперклассе
Рассмотрим набор типов MVP-иш. Существует абстрактный докладчик с интерфейсом View:
public interface View {
//...
}
public abstract class AbstractPresenter<V extends View> {
@Inject V view;
//...
}
Затем давайте укажем конкретный конкретный подкласс-презентатор с его интерфейсом и реализацией:
public interface LoginView extends View {
//...
}
public LoginPresenter extends AbstractPresenter<LoginView> {
//...
}
public class LoginViewImpl implements LoginView {
//...
}
В модуле Dagger, конечно, мы бы определили метод @Provides
:
@Provides
LoginView provideLoginView() {
return new LoginViewImpl();
}
В Guice вы можете написать это так же или просто bind(LoginView.class).to(LoginViewImpl.class)
.
Однако в кинжале (оба v1 и 2.0-SNAPSHOT от Google) это приводит к ошибке, так как не может определить, что V
при создании проводки привязки для AbstractPresenter<V>
. С другой стороны, Гис объясняет это тем, что на самом деле он создает LoginPresenter
, поэтому ему нужна реализация LoginView
.
Кинжал 1.2.2:
foo.bar.AbstractPresenter$$InjectAdapter.java:[21,31] cannot find symbol
symbol: class V
location: class foo.bar.AbstractPresenter$$InjectAdapter
Кинжал 2.0-SNAPSHOT:
Caused by: java.lang.IllegalArgumentException: V
at dagger.internal.codegen.writer.TypeNames$2.defaultAction(TypeNames.java:39)
at dagger.internal.codegen.writer.TypeNames$2.defaultAction(TypeNames.java:36)
at javax.lang.model.util.SimpleTypeVisitor6.visitTypeVariable(SimpleTypeVisitor6.java:179)
at com.sun.tools.javac.code.Type$TypeVar.accept(Type.java:1052)
at dagger.internal.codegen.writer.TypeNames.forTypeMirror(TypeNames.java:36)
at dagger.internal.codegen.MembersInjectorGenerator.write(MembersInjectorGenerator.java:142)
at dagger.internal.codegen.MembersInjectorGenerator.write(MembersInjectorGenerator.java:61)
at dagger.internal.codegen.SourceFileGenerator.generate(SourceFileGenerator.java:53)
at dagger.internal.codegen.InjectBindingRegistry.generateSourcesForRequiredBindings(InjectBindingRegistry.java:101)
at dagger.internal.codegen.ComponentProcessor.process(ComponentProcessor.java:149)
Мой вопрос: Это ошибка? Это недостающая функция? Или это проблема производительности, которую Dagger защищает от (a la SerializableTypeOracleBuilder в GWT RPC)?
Обратите внимание, что эта же проблема возникает, когда V
называется Provider<V>
, Lazy<V>
и т.д.
Ответы
Ответ 1
Это похоже на ошибку, поскольку она не должна генерировать исключение, но она должна регистрировать предупреждение, объясняющее, что параметры типа должны быть привязаны к определенному типу.
Остальное для Dagger2, и я использую 2.1-SNAPSHOT. Вы не представили пример @Component
, который сделает инъекцию, и без него Dagger2 2.1-SNAPSHOT фактически не сообщает о проблеме. Возможно, он уже исправил вашу проблему, и я вижу немного другую версию, но если нет, то я предполагаю, что ваш компонент выглядит примерно так:
@Component
public interface PresenterComponent {
<V extends View> void inject(AbstractPresenter<V> presenter);
}
Когда Dagger2 обрабатывает это, он не может определить конкретный тип для V
, и поэтому он не знает, какой тип вставить. Он не может просто вставить say LoginView
, потому что это сломается, если он был передан a AbstractPresenter<LogoutView>
.
Однако, если вы говорите следующее, Dagger2 может определить, что ему нужно ввести LoginView в AbstractPresenter<LoginView>
и сделать это безопасно.
@Module
public class LoginModule {
@Provides LoginView provideLoginView() {
return new LoginViewImpl();
}
}
@Component(modules = LoginModule.class)
public interface LoginComponent {
void inject(LoginPresenter presenter);
}
Если у вас нет контроля над созданием объекта, например, если некоторые фреймворки создают его для вас и затем передаются для инициализации, гораздо лучше использовать @Inject
в конструкторе, если вы можете, например. например:
public LoginPresenter extends AbstractPresenter<LoginView> {
//...
@Inject LoginPresenter(LoginView view) {
super(view);
//...
}
}
Ответ 2
Это из-за аргументов типа. Injects не работает, если у вас есть аргументы типа. U нужно сделать что-то вроде этого,
bind(new LoginPresenter<LoginViewImpl>(){});