Skip to content

IllegalArgumentException: argument type mismatch": Delegate 'by' pattern breaks when property type is a nullable Value Class #36020

@dreamstar-enterprises

Description

@dreamstar-enterprises

If I have something like this:


@Repository
internal class ContractDomainRepositoryImpl (
    private val contractRepositoryImpl: ContractRepositoryImpl,
    private val contractDomainEntityMapper: ContractDomainEntityMapperImpl,
) : ContractDomainRepository,
    ContractDomainPostRepository by ContractDomainPostRepositoryDelegate(
        contractRepositoryImpl,
        contractDomainEntityMapper
    ),
    ContractDomainPutRepository by ContractDomainPutRepositoryDelegate(
        contractRepositoryImpl,
        contractDomainEntityMapper
    ),
    ContractDomainDeleteRepository by ContractDomainDeleteRepositoryDelegate(
        contractRepositoryImpl
    )

And the interface for ContractDomainPutRepository looks something like:

interface ContractDomainPutRepository {

    suspend fun updateContract(
        contract: Contract,
        operationalData: ContractOperationalData,
        userId: UserId,
        organisationId: OrganisationId?,
        projectId: ProjectId?,
    ): Pair<Contract, ContractOperationalData>

}

And UserId is a value class, like:

@JvmInline
value class UserId(override val value: Long) : Identifier<Long> {
    init {
        // TODO() here it is ok for UserId to be 0, for testing purposes only. Adjust later!
        if (value < 0) {
            throw ValidationError(
                message = "UserId must be a positive value",
            )
        }
    }
},

then trying to call ContractDomainPutRepository.updateContract (from the service layer, that injects ContractDomainRepositoryImpl) will not work

I keep getting Exception caught in ContractDomainServiceImpl: java.lang.IllegalArgumentException: argument type mismatch

Removing the nullable Value Classes, like:

interface ContractDomainPutRepository {

    suspend fun updateContract(
        contract: Contract,
        operationalData: ContractOperationalData,
        userId: UserId,
        organisationId: OrganisationId,
        projectId: ProjectId,
    ): Pair<Contract, ContractOperationalData>

}, 

makes the issue go away

But I need some of value classes to be nullable

The root cause I believe is that Spring's CGLIB proxying doesn't work well with Kotlin value classes since they're implemented as inline classes at compile time and have special JVM representations.

LLM Claude

If you LLM Claude with: "Value classes result it wrong type mismatch error in java class when passed as parameter in spring annotated bean. A plain class works fine. Explain". It says:

When Spring tries to:

Create proxies (CGLIB or JDK proxies)
Inject dependencies
Call methods from Java code

It sees the mangled signatures and underlying primitive types, not the value class wrapper. This causes type mismatches.

Key Takeaway

Value classes are optimized for performance by avoiding object allocation, but this optimization conflicts with Spring's reflection-heavy, proxy-based dependency injection, especially when Java interop is involved. For Spring beans, regular classes are safer unless you carefully manage where value classes are used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions