Enum class with type parameters

Kotlin currently does not support type parameters on enums, but it would be useful to have them.

There has been some discussion here from people missing the same feature in Java:

Could this be possible in Kotlin?

5 Likes

Do you have a good use-case?

The problem I see is that if you manipulate an enum member through the enum type, you canā€™t access the type parameter.

So you only get access to it when you manipulate the enum member by name. But then you can ā€œharcodeā€ the parameter everywhere it is used.

That leaves extending a generic class with different parameters for different enum members. That is indeed Lukasā€™ exemple:

public interface DataType<T> {}

public enum SQLDataType<T> implements DataType<T> {
    TINYINT<Byte>,
    SMALLINT<Short>,
    INT<Integer>,
    BIGINT<Long>,
    CLOB<String>,
    VARCHAR<String>,
    ...
}

But thatā€™s only the definition. How to use that usefully?

1 Like

Iā€™d say SQLDataType is a very good use-case example, as it is directly from jOOQ.

An example of how that is useful: in jOOQ, a query result object has the following method:

<T> Field<T> field(String name, DataType<T> dataType)

So you could use result.field("name", SQLDataType.VARCHAR) to actually get a string,
or result.field("id", SQLDataType.INT) to get an int, etc.

Maybe itā€™s better to use sealed class hierarchy in such case?

sealed class SQLDataType<T : Any>(val datatype: KClass<T>) {
    object TINYINT : SQLDataType<Byte>(Byte::class)
    object SMALLINT : SQLDataType<Short>(Short::class)
    // etc
    object VARCHAR : SQLDataType<String>(String::class)
}
3 Likes

That would be better than using normal class hierarchy, but I think sealed classes are not really suitable if you just need a fixed set of objects. For example, with sealed classes you canā€™t iterate all its objects, or get an objectā€™s ordinal.

Enums would be perfect for these cases, but for some unknown reason Java and Kotlin do not allow type parameters on them. Could it be done, or is there a reason itā€™s not possible?

2 Likes

I know itā€™d be tedious to implement for a lot of instances, but you could add an iterative accessor, ordinal accessor, and some other stuff (i.e. valueOf or something) manually. Not a great solution of course, but it could serve as a workaround at least.

1 Like

I REALLY WANT THIS!!

consider this enum:

enum class Conjunction(private val pred: (Predicate<Any>, Predicate<Any>) -> Predicate<*>) : (Predicate<Any>, Predicate<Any>) -> Predicate<*> 
{
    AND({ p1, p2 -> p1.and(p2) }),
    OR({ p1, p2 -> p1.or(p2) });
    override fun invoke(p1: Predicate<Any>, p2: Predicate<Any>) = pred(p1, p2)
}

Absence of type parameters makes it basically unusable. Could I use type Parameters, then I would replace Any with T, but I canā€™t, so I have to resort to a normal class nowā€¦

3 Likes

No news on this? It still bugs me regularlyā€¦

This subject seems related to JEP 301: Enhanced Enums proposal.

Right, you donā€™t have ordinals like in enums, but it is possible to iterate sealed objects:

private sealed class SealedValue {
	object A : SealedValue()
	object B : SealedValue()
	object C : SealedValue()
}

fun main() {
	val sealedValues = SealedValue::class.sealedSubclasses.mapNotNull { it.objectInstance }
	sealedValues.forEach(::println)
}
2 Likes

To have a logically related iterable set of objects (without having to use reflection) I just wrote the following helper base class:

open class EnumObjectList<T> private constructor( private val list: MutableList<T> ) :
    List<T> by list
{
    constructor() : this( mutableListOf() )

    protected fun <TAdd : T> add( item: TAdd ): TAdd = item.also { list.add( it ) }
}

Which for example can be used as follows:

object SamplingSchemes : EnumObjectList<DataTypeSamplingScheme<*>>()
{
    val GEOLOCATION = add( Geolocation( TimeSpan.fromMinutes( 1.0 ) ) )
    val STEPCOUNT = add( Stepcount( TimeSpan.fromMinutes( 1.0 ) ) )
}

The members retain their full type information, and there is no need for an ā€˜intermediateā€™ enum type.

3 Likes

I wrote the full reasoning behind this up in a blog post: List of Strongly-Typed Objects Acting Like Enum in Kotlin ā€“ whatheco.de

4 Likes

My personal opinion very much instersects with what @Whathecode says. Antlr (Parser Generator) for example makes heavy use of that to prevent reflecting over types. So, parsers would be a good use case I guess.

Also I think that would be a very usable feature for when statements and pattern matching.
So primary purpose is probably reducing reflecting code. Which is great for native, too, no?

3 Likes