我已经盯着这个问题很长一段时间了,一直无法找到答案。我正在尝试使用mapstruct来映射具有通用SingleValue对象实现的字段的对象。
让我用简化的例子说明问题。 (为了便于阅读,省略了抛出)
我们有一个Single Value对象:
public abstract class SingleValue<T extends Serializable> implements Serializable {
private T value;
public SingleValue(T value) {
this.value = value;
}
}
具体的单值类看起来像这样
public class FirstName extends SingleValue<String>{
}
并将用于可能相互映射的不同对象
应该可能的映射:
使用当前解决方案可以进行以下映射:
当前单值映射器
@Mapper
public interface SingleValueMapper {
SingleValueMapper INSTANCE = Mappers.getMapper(SingleValueMapper.class);
default <Z extends Serializable, T extends SingleValue<Z>> T singleValue(Z obj, Class<Z> objClass, @TargetType Class<T> targetType) {
if (obj == null) {
return null;
}
return targetType.getConstructor(objClass).newInstance(obj);
}
default <T extends Serializable, Z extends SingleValue<T>> T getValue(Z singleValue) {
if (singleValue == null) {
return null;
}
return singleValue.getValue();
}
default <T extends SingleValue<String>> T singleValue(String obj, @TargetType Class<T> targetType) {
return singleValue(obj, String.class, targetType);
}
}
对于FirstName - >另一个扩展的SingleValue对象映射,我提出了以下解决方案:
default <T extends SingleValue<String>> T stringSingleValue(SingleValue<String> obj, @TargetType Class<T> targetType)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return singleValue(obj.getValue(), String.class, targetType);
}
我上面提到的映射方法存在的问题:
当将String映射到A扩展SingleValue时,此调用由mapstruct生成:
A a = singleValueMapper.singleValue( client.getCompanyName(), A.class )
这个调用不是由mapstruct选择的,但我们可以在手动使用@Mapping作为表达式时使用它。但是我们希望mapstruct在映射A时将自动映射这些调用,将SingleValue扩展为B扩展SingleValue:
B b =singleValueMapper.stringSingleValue(command.getTitle(), B.class)
有没有办法用mapstruct和java泛型来解决这个问题?
我正在使用Mapstruct 1.3.0版本的版本。
我需要一些地方来提供一些反馈。
@Mapper // Why is this annotated with @Mapper or even an interface (iso a regular class)? Are there more methods in here not shown that should be generated by MapStruct?
public interface SingleValueMapper {
SingleValueMapper INSTANCE = Mappers.getMapper(SingleValueMapper.class);
// How should MapStruct know how to populate these? It can only map a source to a target? It takes @TargetType as hint, but it cannot populate the second argument Class<Z> ojbClass. But is this required? Perhaps obj.getClass()?
default <Z extends Serializable, T extends SingleValue<Z>> T singleValue(Z obj, Class<Z> objClass, @TargetType Class<T> targetType) {
if (obj == null) {
return null;
}
return targetType.getConstructor(objClass).newInstance(obj);
}
default <T extends Serializable, Z extends SingleValue<T>> T getValue(Z singleValue) {
if (singleValue == null) {
return null;
}
return singleValue.getValue();
}
// seems to me like a duplicate of the above?
default <T extends SingleValue<String>> T singleValue(String obj, @TargetType Class<T> targetType) {
return singleValue(obj, String.class, targetType);
}
}
所以有了上面的内容,我会像这样重写这个类:
public class SingleValueMapper {
<Z extends Serializable, T extends SingleValue<Z>> T singleValue(Z obj, @TargetType Class<T> targetType) {
if (obj == null) {
return null;
}
return targetType.getConstructor(obj.getClass()).newInstance(obj);
}
<T extends Serializable, Z extends SingleValue<T>> T getValue(Z singleValue) {
if (singleValue == null) {
return null;
}
return singleValue.getValue();
}
}