繁体   English   中英

Java bean 映射器预期捕获但提供对象

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM