【问题标题】:Mapstruct: How to map A extends SingleValue<T> to T and A extends SingleValue<T> to B extends SingleValue<T> with the same mapper?Mapstruct:如何使用相同的映射器将 A 扩展 SingleValue<T> 映射到 T 和 A 扩展 SingleValue<T> 到 B 扩展 SingleValue<T>?
【发布时间】:2019-07-16 17:48:05
【问题描述】:

我已经关注这个问题很长时间了,但一直无法找到答案。我正在尝试使用 mapstruct 映射具有作为通用 SingleValue 对象实现的字段的对象。

让我用一个简化的例子来说明这个问题。 (为了便于阅读,省略了抛出)

我们有一个单值对象:

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>{

}

并且将用于可能相互映射的不同对象

应该可能的映射:

  • 字符串 -> 名字
  • 名字 -> 字符串
  • FirstName -> 另一个扩展 SingleValue 对象

当前解决方案可以进行以下映射:

  • 字符串 -> 名字
  • 名字 -> 字符串

当前单值映射器

@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);
}

上面提到的映射方法存在的问题:

  • 在自动选择中,Mapstruct 将更喜欢 getValue(Z singleValue) 映射而不是这个映射(我真的不想使用限定符,因为那样我需要限定每个映射的字段)
  • 例如,如果我们还为 UUID 单个值添加一个映射方法,Mapstruct 会认为映射方法不明确

在将 String 映射到 A extends SingleValue 时,mapstruct 会生成此调用:

A a = singleValueMapper.singleValue( client.getCompanyName(), A.class )

此调用不是由 mapstruct 选择的,但我们可以在手动使用它时使用它,并将 @Mapping 作为表达式。但是,我们希望 mapstruct 在将 A extends SingleValue 映射到 B extends SingleValue 时自动映射这些调用:

B b =singleValueMapper.stringSingleValue(command.getTitle(), B.class)

有没有办法使用 mapstruct 和 java 泛型来解决这个问题?

我正在使用 Mapstruct 版本 1.3.0-final。

【问题讨论】:

  • 您使用的是哪个版本的 MapStruct?
  • 版本 1.3.0-final
  • 您的第一个方法似乎有 3 个参数.. ` default > T singleValue(Z obj, Class objClass, @TargetType Class targetType)` MapStruct 只能选择基于源/目标的映射方法。不能基于 2 个源。但是,它可以基于多个来源构建类..这是您的意图吗?实际上是@ObjectFactory吗?
  • 在这种情况下很难为您提供帮助。您能否也提供此示例的调用映射器。?
  • @Sjaak 在回答您的问题时,我意识到我的问题中有一个错误,我想到但没有奏效的解决方案不是正确的代码 sn-p 我在问题中解决了这个问题。 SingleValueMapper 之外的其他映射器永远不会调用具有 3 个参数的方法。在上面的示例中,它仅被称为具有 2 个参数的方法,即 'default > T singleValue(String obj, '@'TargetType Class targetType)' 方法。我在问题中添加了呼叫示例和一些描述性文本

标签: java generics mapstruct


【解决方案1】:

我需要一些地方来提供一些反馈。

@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();
    }

}

【讨论】:

  • Sjaak je bent een 持有:)。这简化了分配,这很棒。但是,使用此解决方案时,将 A extends SingleValue 映射到 B extends SingleValue 时仍然会出错。我得到的错误是 getValue() 和 singleValue() 方法之间存在不明确的映射。有没有办法在不使用限定符的情况下解决这个问题? (因为这意味着我们需要在几乎所有地方使用限定符,因为我们分配了单值映射)
  • 当你去掉泛型时,得到的方法是:SingleValue singleValue(Serializable obj @TargetType Class&lt;Singlevalue&gt; targetType) 和`Serializable getValue(SingleValue singleValue)` 对吗?问题是SingleValue 也是Serializable.. 也许您需要不同的标记接口来表达SingleValue 的值。两个候选者都适合将 wrt 映射到 MapStruct。但是,您可以使用@Mapping#resultType..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-03
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
相关资源
最近更新 更多