[英]Java bean mapper expected capture but is provided object
请注意:即使我在这个问题中提到了Dozer ,我确实相信它实际上只是一个纯 Java 泛型问题。 那里可能有一个特定于 Dozer 的解决方案,但我认为任何对 Java (11) 泛型/捕获/擦除有很强的工作知识的人都应该能够帮助我!
Java 11 和推土机在这里。 Dozer 非常适合将默认 bean 映射规则应用于字段名称,但只要您有专门的自定义映射逻辑,您就需要实现 Dozer CustomConverter
并注册它。 那就太好了,除了CustomConverter
的 Dozer API 没有通用化,是单片的并且会导致像这样的讨厌的代码:
public class MyMonolithicConverter implements CustomConverter {
@Override
public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
if (sourceClass.isAssignableFrom(Widget.class)) {
Widget widget = (Widget)source;
if (destinationClass.isAssignableFrom(Fizz.class)) {
Fizz fizz = (Fizz)destination;
// write code for mapping widget -> fizz here
} else if (destinationClass.isAssignableFrom(Foo.class)) {
// write code for mapping widget -> foo here
}
... etc.
} else if (sourceClass.isAssignableFrom(Baz.class)) {
// write all the if-else-ifs and mappings for baz -> ??? here
}
}
}
再说一遍:整体的,没有泛化的,并导致大型、复杂的嵌套 if-else-if 块。 哎呀。
我试图让这个更可口:
public abstract class BeanMapper<SOURCE,TARGET> {
private Class<SOURCE> sourceClass;
private Class<TARGET> targetClass;
public abstract TARGET map(SOURCE source);
public boolean matches(Class<?> otherSourceClass, Class<?> otherTargetClass) {
return sourceClass.equals(otherSourceClass) && targetClass.equals(otherTargetClass);
}
}
然后,它的一个例子:
public class SignUpRequestToAccountMapper extends BeanMapper<SignUpRequest, Account> {
private PlaintextEncrypter encrypter;
public SignUpRequestToAccountMapper(PlaintextEncrypter encrypter) {
this.encrypter = encrypter;
}
@Override
public Account map(SignUpRequest signUpRequest) {
return Account.builder()
.username(signUpRequest.getRequestedName())
.email(signUpRequest.getEmailAddr())
.givenName(signUpRequest.getFirstName())
.surname(signUpRequest.getLastName()())
.dob(DateUtils.toDate(signUpRequest.getBirthDate()))
.passwordEnc(encrypter.saltPepperAndEncrypt(signUpRequest.getPasswordPlaintext()))
.build();
}
}
现在有一种方法可以从我的推土机转换器内部调用正确的源 -> 目标映射器:
public class DozerConverter implements CustomConverter {
private Set<BeanMapper> beanMappers;
@Override
public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
BeanMapper<?,?> mapper = beanMappers.stream()
.filter(beanMapper -> beanMapper.matches(sourceClass, destinationClass))
.findFirst()
.orElseThrow();
// compiler error here:
return mapper.map(source);
}
}
我真的很喜欢这种设计/API 方法,但是最后在mapper.map(source)
行上出现编译器错误:
“所需类型:捕获?;提供:对象”
我能做些什么来修复这个编译器错误? 我不喜欢这种 API/方法,但我确实喜欢它在上面的MyMonolithicConverter
示例中添加的简单性,这是 Dozer 对你施加的一种方法。 需要注意的是,我在其他地方使用 Dozer 进行简单的 bean 映射,因此我更愿意使用CustomConverter
impl 并为此利用 Dozer,而不是为这些自定义/复杂映射引入一个完整的其他依赖项/库。 如果 Dozer 提供不同的解决方案,我可能也会对此感到满意。 否则我只需要修复这个捕获问题。 感谢您在这里的任何帮助!
问题似乎来自beanMappers
。 您有一组不同类型的映射器。 编译器无法推断找到的mapper
将具有哪些类型。
您可以通过强制转换结果并抑制它给您的警告来使编译器相信您。
转换为<?,?>
不会发生,所以我为 convert 方法添加了符号。 至少可以假设当你得到一个BeanMapper<S,T>
时, map
确实会在S
源上返回一个T
。
class DozerConverter {
private Set<BeanMapper<Object,Object>> beanMappers;
public <S,T> T convert(S source,
Class<?> destinationClass,
Class<?> sourceClass) {
@SuppressWarnings("unchecked")
BeanMapper<S,T> mapper = (BeanMapper<S,T>) beanMappers.stream()
.filter(beanMapper -> beanMapper.matches(sourceClass, destinationClass))
.findFirst()
.orElseThrow();
return mapper.map(source);
}
}
恐怕您将不得不这样称呼它:
TARGET-TYPE target = dozerConverter.<SOURCE-TYPE,TARGET-TYPE>convert(...);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.