Vaadin CRM Demo创建抽象表单类触发ComponentEventListener类型推断错误
问题根因
报错本质是泛型类型不匹配+父类成员隐藏未初始化两个问题共同导致的:
ContactForm中重复定义了Binder<Contact> binder,隐藏了父类AbstractForm中定义的Binder<T> binder,父类方法调用的是未初始化的父类binder,同时事件绑定过程中泛型推导失败- 事件内部类的泛型定义不严谨,导致
addListener调用时无法匹配方法参数的事件类型
修复步骤
1. 调整AbstractForm的成员访问权限,修正事件泛型
修改AbstractForm类代码:
public abstract class AbstractForm<T> extends FormLayout { public T entity; // 将binder改为protected,允许子类初始化 protected Binder<T> binder; Button save = new Button("Save"); Button delete = new Button("Delete"); Button close = new Button("Cancel"); // 其余原有代码保持不变,仅修改事件类定义部分 public abstract class EntityFormEvent extends ComponentEvent<AbstractForm<T>> { private final T entity; // 构造方法source添加泛型约束 protected EntityFormEvent(AbstractForm<T> source, T entity) { super(source, false); this.entity = entity; } public T getEntity() { return entity; } } public class SaveEvent extends EntityFormEvent { SaveEvent(AbstractForm<T> source, T entity) { super(source, entity); } } public class DeleteEvent extends EntityFormEvent { DeleteEvent(AbstractForm<T> source, T entity) { super(source, entity); } } public class CloseEvent extends EntityFormEvent { CloseEvent(AbstractForm<T> source) { super(source, null); } } }
2. 删除ContactForm中重复的binder定义,初始化父类binder
修改ContactForm类代码,移除自身重复定义的binder,直接给父类binder赋值:
public class ContactForm extends AbstractForm<Contact> { TextField firstName = new TextField("First name"); TextField lastName = new TextField("Last name"); EmailField email = new EmailField("Email"); ComboBox<Status> status = new ComboBox<>("Status"); ComboBox<Company> company = new ComboBox<>("Company"); public ContactForm(List<Company> companies, List<Status> statuses) { addClassName("contact-form"); // 直接初始化父类的binder,不要重复声明 binder = new BeanValidationBinder<>(Contact.class); binder.bindInstanceFields(this); company.setItems(companies); company.setItemLabelGenerator(Company::getName); status.setItems(statuses); status.setItemLabelGenerator(Status::getName); add(firstName, lastName, email, company, status, createButtonsLayout()); } }
3. 调整ListView中事件方法的参数类型
把saveContact、deleteContact的参数类型调整为对齐父类泛型的事件类型:
private void saveContact(AbstractForm<Contact>.SaveEvent event) { service.saveContact(event.getEntity()); updateList(); closeEditor(); } private void deleteContact(AbstractForm<Contact>.DeleteEvent event) { service.deleteContact(event.getEntity()); updateList(); closeEditor(); }
如果仍然存在泛型推导问题,可在addListener调用时显式指定泛型:
form.<AbstractForm<Contact>.SaveEvent>addListener(AbstractForm.SaveEvent.class, this::saveContact); form.<AbstractForm<Contact>.DeleteEvent>addListener(AbstractForm.DeleteEvent.class, this::deleteContact); form.<AbstractForm<Contact>.CloseEvent>addListener(AbstractForm.CloseEvent.class, e -> closeEditor());
修改后重新构建即可解决报错,此时父类binder已正确初始化,事件类型完全对齐,ComponentEventListener的泛型可以正常推导。
内容的提问来源于stack exchange,提问作者Lewis Liu
相关产品推荐
相关产品推荐

