我有以下类结构(代码在最后):
abstract class AbstractThing
:我有一些具有共同领域的不同种类的东西class Foo extends AbstractThing
:foo 是一种东西abstract class AbstractReq <T extends AbstractThing>
:我有与事物相关的请求,其中请求的主题是一个字段 (T subject
)FooReq extends AbstractReq<Foo>
:关于 foos 的请求,特别是FooReqDto
:一个 DTO 类,所有字段都在 FooReq
中,扁平化我正在尝试使用 ModelMapper 将
FooReq
的实例映射到 FooReqDto
。如果我不需要自定义类型映射,那么一切都会很好。但我确实(在我的实际代码中)需要自定义一些东西,由名为 somethingDifferent
的字段表示,而不是 subjectFooField
中的 FooReqDto
。现在,当我运行测试时,出现以下错误:
java.lang.ClassCastException:类 com.example.test.TempTest$AbstractThing$ByteBuddy$PsTTXfw5 无法转换为类 com.example.test.TempTest$Foo (com.example.test.TempTest$AbstractThing$ByteBuddy$PsTTXfw5 和com.example.test.TempTest$Foo 位于加载程序“app”的未命名模块中)
创建类型映射时我缺少什么?涉及继承和泛型的映射类似乎不存在任何固有的问题,因为它在没有映射的情况下工作。在我提取抽象类之前,我使用的实际映射工作得很好(这对于减少大量重复代码是必要的)。
完整代码如下:
package com.example.test;
import org.junit.Test;
import org.modelmapper.ModelMapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TempTest {
@Test
public void testStuff() {
final ModelMapper mapper = new ModelMapper();
mapper.createTypeMap(FooReq.class, FooReqDto.class)
.addMappings(map -> {
map.map(req -> req.getSubject().getFooField(), FooReqDto::setSomethingDifferent);
});
final FooReq req = new FooReq();
req.setSubmitted(true);
req.getSubject().setName("testFoo");
req.getSubject().setFooField("bar");
final FooReqDto dto = mapper.map(req, FooReqDto.class);
assertTrue(dto.isSubmitted());
assertEquals("testFoo", dto.getSubjectName());
assertEquals("bar", dto.getSomethingDifferent());
}
public static abstract class AbstractThing {
private String name;
// getters and setters omitted
}
public static class Foo extends AbstractThing {
private String fooField;
// getters and setters omitted
}
public static abstract class AbstractReq <T extends AbstractThing> {
protected T subject;
private boolean submitted;
// getters and setters omitted
}
public static class FooReq extends AbstractReq<Foo> {
public FooReq() {
super();
subject = new Foo();
}
}
public static class FooReqDto {
private boolean submitted;
private String subjectName;
private String somethingDifferent;
public FooReqDto() {
}
// getters and setters omitted
}
}
对此的一个快速解决方法是向
FooReq
添加显式设置器:
public static class FooReq extends AbstractReq<Foo> {
public FooReq() {
super();
subject = new Foo();
}
public Foo getSubject() { return subject; }
public void setSubject(Foo subject) { this.subject = subject; }
}
它对于父 getter 和 setter 来说是多余的,但可以防止 ModelMapper 在使用反射时感到困惑并在运行时丢失通用信息。
如果父类的唯一目标是减少模型类本身的重复,那么这基本上会达不到目的。就我而言,此设置是一个工具,因此我可以完成其他事情,因此此解决方案适合我。不过,我有兴趣看到仅通过调整 ModelMapper 配置即可工作的其他选项。