如何实例化继承Java基类的Kotlin实体类并设置基类字段
Kotlin继承Java基类后设置父类字段的解决方案
问题背景
项目中所有实体类需继承定义了identifier、version等公共字段的Java基类BaseEntity,用Kotlin编写的MyEntity继承该基类后,单元测试时无法直接通过构造器传入父类字段,用apply赋值又提示val cannot be reassigned错误。Java类可通过Lombok的@SuperBuilder解决,但不知道Kotlin中如何操作。
相关代码
Java基类BaseEntity:
@Getter @SuperBuilder(toBuilder = true) @MappedSuperclass @EqualsAndHashCode(onlyExplicitlyIncluded = true, doNotUseGetters = true) @NoArgsConstructor @AllArgsConstructor @Audited @ToString public class BaseEntity { @Id @GeneratedValue @Column( name = "identifier", nullable = false, columnDefinition = "uuid" ) private UUID identifier; @Version @Column( name = "vrsn", nullable = false ) private long version; // 其他公共字段 }
Kotlin实体类MyEntity:
@Entity @Audited @Table(name = "my_entity") class MyEntity( val identityDocumentNumber: String?, val startDate: Instant?, val endDate: Instant?, @Enumerated(EnumType.STRING) val enum: AnEnum, val email: String, ) : BaseEntity()
尝试过的错误写法
直接在构造器传入父类字段:
private val MOCK_MY_ENTITY = MyEntity( identifier = MOCK_ANALYST_NAME, // 错误:找不到该参数 identityDocumentNumber = null, startDate = Instant.now(), // 其他参数 )
用apply赋值:
private val MOCK_MY_ENTITY = MyEntity( identityDocumentNumber = null, startDate = Instant.now(), // 其他参数 ).apply { identifier = MOCK_ANALYST_UID // 错误:val无法重新赋值 }
Java中可行的写法:
private static final MyEntity MY_ENTITY = MyEntity.builder() .identifier(MOCK_IDENTIFIER_UUID) .identityDocumentNumber(MOCK_DOCUMENT_NUMBER) // 其他参数 .build();
解决方案
1. 修改Kotlin实体类,显式调用父类全参构造器
利用BaseEntity的@AllArgsConstructor生成的全参构造器,在Kotlin类中显式传入父类字段:
@Entity @Audited @Table(name = "my_entity") class MyEntity( val identityDocumentNumber: String?, val startDate: Instant?, val endDate: Instant?, @Enumerated(EnumType.STRING) val enum: AnEnum, val email: String, identifier: UUID, version: Long = 0L // 测试时可给版本号默认值 ) : BaseEntity(identifier, version, /* 其他父类字段的默认值或必填参数 */)
创建实例时直接传入identifier:
private val MOCK_MY_ENTITY = MyEntity( identityDocumentNumber = null, startDate = Instant.now(), endDate = null, enum = AnEnum.SOME_VALUE, email = "test@example.com", identifier = MOCK_ANALYST_UID )
2. 兼容Lombok的@SuperBuilder
给Kotlin的MyEntity添加@Builder注解(需确保项目配置了Lombok对Kotlin的支持),然后直接调用Builder:
@Entity @Audited @Table(name = "my_entity") @Builder // 添加Lombok的Builder注解 class MyEntity( val identityDocumentNumber: String?, val startDate: Instant?, val endDate: Instant?, @Enumerated(EnumType.STRING) val enum: AnEnum, val email: String, ) : BaseEntity()
使用Builder构造实例:
private val MOCK_MY_ENTITY = MyEntity.builder() .identifier(MOCK_ANALYST_UID) .identityDocumentNumber(null) .startDate(Instant.now()) .enum(AnEnum.SOME_VALUE) .email("test@example.com") .build()
3. 反射赋值(仅测试环境使用)
在单元测试中,通过反射绕过val的限制,直接修改父类字段:
private val MOCK_MY_ENTITY = MyEntity( identityDocumentNumber = null, startDate = Instant.now(), endDate = null, enum = AnEnum.SOME_VALUE, email = "test@example.com" ).apply { val identifierField = BaseEntity::class.java.getDeclaredField("identifier") identifierField.isAccessible = true identifierField.set(this, MOCK_ANALYST_UID) // 如需设置version字段同理 val versionField = BaseEntity::class.java.getDeclaredField("version") versionField.isAccessible = true versionField.set(this, 1L) }
⚠️ 注意:此方法仅适合测试环境,生产环境禁止使用,会破坏封装性。
4. 使用Mock框架(Mockito)
如果测试仅需要模拟实体的行为而非真实实例,用Mockito直接Mock:
import org.mockito.Mockito.mock import org.mockito.Mockito.`when` private val MOCK_MY_ENTITY = mock(MyEntity::class.java).apply { `when`(identifier).thenReturn(MOCK_ANALYST_UID) `when`(identityDocumentNumber).thenReturn(null) `when`(startDate).thenReturn(Instant.now()) // 其他字段的模拟 }
这种方式适合不需要完整实体实例的测试场景,避免构造真实对象的繁琐。
内容的提问来源于stack exchange,提问作者RKA
相关产品推荐
相关产品推荐

