請注意:即使我在這個問題中提到了Dozer,我確實相信它實際上只是一個純 Java 泛型問題。那里可能有一個特定于 Dozer 的解決方案,但我認為任何對 Java (11) 泛型/捕獲/擦除有很強的作業知識的人都應該能夠幫助我!
Java 11 和推土機在這里。Dozer 非常適合將默認 bean 映射規則應用于欄位名稱,但只要您有專門的自定義映射邏輯,您就需要實作 DozerCustomConverter并注冊它。那就太好了,除了 Dozer APICustomConverter沒有通用化,是單片的并且會導致像這樣的討厭的代碼:
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);
}
}
我真的很喜歡mapper.map(source)這種設計/API 方法,但是最后我在該行收到編譯器錯誤:
“所需型別:捕獲?;提供:物件”
我能做些什么來修復這個編譯器錯誤?我不喜歡這種 API/方法,但我確實喜歡它在MyMonolithicConverter上面的示例中添加的簡單性,這是 Dozer 對你施加的一種方法。需要注意的是,我在其他地方使用 Dozer 進行簡單的 bean 映射,因此我更愿意使用CustomConverterimpl 并利用 Dozer 來實作這一點,而不是為這些自定義/復雜映射引入一個完整的其他依賴項/庫。如果 Dozer 提供不同的解決方案,我可能也會對此感到滿意。否則我只需要修復這個捕獲問題。感謝您在這里的任何幫助!
uj5u.com熱心網友回復:
問題似乎來自beanMappers. 您有一組不同型別的映射器。編譯器無法推斷找到的型別mapper將具有哪些型別。
您可以通過強制轉換結果并抑制它給您的警告來使編譯器相信您。
<?,?>不會強制轉換為 a ,因此我為 convert 方法添加了符號。至少可以假設當你得到 a 時BeanMapper<S,T>,map確實會T在S源上回傳 a 。
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(...);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/494523.html
