Unchecked cast while overriding return value by subtype

I have the following generic interface

interface DataFormatter<T: Any> {
    val kClass: KClass<T>

    fun readFromPath(path: Path): T
    fun writeToPath(path: Path, data: T)
}

And an object which implements it

object TextFormatter : DataFormatter<String> {

  override val kClass: KClass<String> = String::class

  override fun readFromPath(path: Path): String = path.readText()
  override fun writeToPath(path: Path, data: String) = path.writeText(data)
}

I also have an interface with generic function which returns DataFormatter<T>

interface FormatterLookup {
  fun <T: Any> formatter(): DataFormatter<T>
}

Now I want to create enum with members implementing this interface for particular DataFormatter<T>, like this

enum class DataTypes : FormatterLookup {
  @Suppress("UNCHECKED_CAST")
  TEXT {
    override fun <String: Any> formatter(): DataFormatter<String> = TextFormatter as DataFormatter<String>
  }
}

But the compiler reports unchecked cast which I have to suppress. What is the reason and how can I avoid this situation?

The first thing that leaps out at me is the <String : Any>: it declares a type parameter called ‘String’, as some subtype of ‘Any’. (That’s not related to the normal ‘String’ type.) It then defines the formatter() override in terms of that type parameter.

I’m guessing that’s not what you intended


Your formatter function doesn’t really make sense to me. It is generic and that means the caller provides the T. How could implementation of FormatterLookup provide exactly the formatter needed by the caller, and especially if the lookup object doesn’t even know what is T?

Did you maybe mean to make the FormatterLookup generic, instead of its function? This way implementations could be related to specific T.

1 Like

<String : Any> : it declares a type parameter called ‘String’, as some subtype of ‘Any’

Yes, you a right
 In fact, it is the same as

override fun <T: Any> formatter(): DataFormatter<T> = TextFormatter as DataFormatter<T>

But what is the correct way to override this function with a concrete value of then, namely String class?

I would love to do it, but enum can’t implement generic interfaces because enums can’t be generic, right? Or am I missing something?

In my idea, each enum member should have either property or function which returns its a specific object implementing DataFormatter<T> interface of a specific type T. So, a enum member knows a specific T and can implement formatter() for this specific type.

But it seems this trick doesn’t work. Any other ideas how to overcome the limitation of non-generic enums?

Yes, you are correct, enums are limited. Is there any specific reason why you need enums and you can’t use a singleton object or sealed class?

Also, frankly speaking I’m a little confused with your design. How do you plan to access these formatters in the code? If your case is that at some point we need a formatter specifically for type String, so the caller knows the data type, then we can use the TextFormatter directly, we don’t need this DataTypes.TEXT. If your case is that the caller doesn’t know T, e.g. we have a KClass and we need a formatter for it, then we can’t use this DataTypes anyway. So what benefits do we get from having the DataTypes? I don’t say we don’t get any, I’m just curious about use cases.

Thank you for your interest! Yes, you are right, it makes sense to clarify the overall idea.

I want to read from a YAML/JSON file some metadata about other files in the same folder (let’s name it metadata file). One of the atributes is a String value that is planned to be de-serialized to an enum value, which holds information about particular formatter to read data from this file (or write this file).

Moreover, this is a library, and the enum was supposed to be provided by a user of the library. So, I was planning to de-serialize metadata file to an object of a generic class with a specific user-provided enum implementing the DataFormatter<T> interface.

The ultimate goal is to have a library that will allow its users:

  1. to define a list of supported data formats along with the corresponding formatters
  2. to store information about formats of specific files in a library-defined metadata file
  3. to read/write files of specified formats to Kotlin objects in a typesafe way

But it seems I wanted too much or missed something important from Kotlin, or most probably both, lol.

Hmm, so this is actually my point. If T is always dynamic in your case, then you don’t really benefit from having this strongly-typed DataTypes class. And it adds boilerplate and complicates the logic. You can have a single, fully dynamic class:


fun main() {
    val reg = FormattersRegistry()
    reg.register(TextFormatter)
    reg.register(DateFormatter)
    
    reg.read(String::class, ...)
}

class FormattersRegistry {
    fun register(formatter: DataFormatter<*>) { ... }
    fun <T : Any> read(type: KClass<T>, path: Path): T { ... }
    fun write(data: Any, path: Path) { ... }
}

Of course we can create a function which initializes the registry and adds all formatters. Is there anything you like to do, but can’t with this approach?

I’m not sure if I completelly understand the suggested (for example, what register() function actually does and why read() needs type: KClass<T> as an argument if we already registered a relevant formatter), but I think I got your general idea.

So, I could have something like

fun FormatterFactory(type: DataTypes): DataFormatter<*> =
  when(type) {
    DataTypes.TEXT -> TextFormatter
  }

The user will provide to the library

  • a simple enum that doesn’t implement any interface
  • a factory (as a lambda, for example) of type (DataTypes)-> DataFormatter<*>

The library will use the provided enum and factory to read data from files, but the user still needs to know the type of data in a file and use it for values which will be serialized/de-serialized to/from this file. I missed the last piece in my initial idea, thank you for pointing it out!

My main point is: do we even need to require this enum? User of your lib has to first create formatters (e.g. TextFormatter, DateFormatter), then they additionally need to create enum fields (e.g. DataTypes.TEXT, DataTypes.DATE), then they probably initialize your lib with something like: initialize(DataTypes::class). And as we can see, we have some problems due to using enums that have some restrictions.

Why not simply: initialize(TextFormatter, DateFormatter) and skip the enum part entirely? Or if we need to specify formatter names/ids: initialize("text" to TextFormatter, "date" to DateFormatter).

I need enum to serialize data types to the metadata file (maintened by the library, not user). I no longer need to implement any functions in those enums, but they can have some simple properties, like file extension, for example.

initialize("text" to TextFormatter, "date" to DateFormatter)

I would prefer using enums instead of strings like text and date in such maps because enums are more convinient in when statements.

But wouldn’t it be enough if DataFormatter had val id: String (or name)? By having a list of such formatters, we can create a metadata file and we can find a formatter for a specific id/name.

So I’m back at the question if you ever need such a compile-time formatter resolution? Because all you said feels like the list of formatters is dynamic in your case. Formatters are provided by the user code, library doesn’t know them, so such when code is impossible at least in the library code anyway. So to simplify my question: do you ever need to write a code which “knows” very specific formatters and provides different behavior depending on the chosen formatter? Does this happen in the library or in the user code?

Conceptually it doesn’t matter if you define mapping between label and class in enumerator or by registry. The actual problem you facing is the type erasure: JVM doesn’t know about generics and won’t be able to cast classes correctly. This is also the reason you need to provide the Class parameter to the signature of the method.
I would suggest to request from the application the map of label to Class type (as enum, registry, or simply a Map<String, Class>) and generic method, which takes that Class as a first parameter and returns the instance of T.
Inside this method they can use switch to call individual converting methods base on the Class parameter value.

Another variation would be to make this method inline. In that case it will have access to the type from the calling site without getting it explicitly as a parameter.

The enum value may define the abstract method, returning Any, or each element will implement its own method, returning its own type. In both cases the application code should “know” the type in the file. This is what you try to avoid.