Kotlin, Spring Boot, JPA - take value of a Generic Enum (E.valueOf(stringValue))

Viewed 327

Background

I'm developing a Spring Boot application and I'm using Kotlin, IntelliJ and Gradle (Groovy). I have some enum class in my code and I need to persist them (with JPA). I used a simple global converter.

// Sample Enum
enum class Policy {
    PUBLIC,
    INVITE_ONLY
}

// Sample Converter
@Converter(autoApply = true)
class PolicyConverter : AttributeConverter<Policy, String> {
    override fun convertToDatabaseColumn(attribute: Policy): String {
        return attribute.name
    }
    override fun convertToEntityAttribute(dbData: String): Policy {
        return Policy.valueOf(dbData.toUpperCase())
    }
}

Problem

Since I have 5-6 enums and I hate duplicated code, I thought about a generic converter that should do the work for every given enum. I tried to code something, but nothing worked. This is what I was thinking about:

abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
    override fun convertToDatabaseColumn(attribute: E): String {
        return attribute.name
    }

    override fun convertToEntityAttribute(dbData: String): E {
        return E.valueOf(dbData.toUpperCase())
    }
}

In this way I can only extend from one abstract class every enum converter, like so:

@Converter(autoApply = true)
class PolicyConverter : EnumConverter<Policy>() {}

Problem with this code is that I have two errors:

  • E is red because: Type parameter 'E' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
  • valueOf is red because: unresolved reference (there are like 150+ types of .valueOf).

As suggested from this I tried to use following function:

private inline fun <reified E : Enum<E>> getValue(string: String): E {
    return enumValueOf(string.toUpperCase())
}

But when called from the .convertToEntityAttribute, the result is that "Cannot use 'E' as reified type parameter. Use a class instead."

Question

So the question is simple: how can I implement an easy and fast way to make one converter for all my enums, that all follows the same principle? I just need a return E.valueOf(<value>) function, but it's not present.

1 Answers

A simply workaround of this problem is to define an abstract method that every class will implement and it will return the correct type, given a string.

// Inside EnumConverter, the Generic Class
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
    abstract fun getValueFromString(string: String) : E

    override fun convertToEntityAttribute(dbData: String): E {
        return getValueFromString(dbData)
    }
    [...]
}

// Inside Policy Enum, implementing
class PolicyConverter : EnumConverter<Policy>() {
    override fun getValueFromString(string: String): Policy {
        return Policy.valueOf(string.toUpperCase())
    }
}

But it's a workaround that I really dislike.

Related