Using generics (type parameters) in exceptions


#1

Hello:

I would like to know if there is any plan to allow the use of generics in exceptions, in future releases of Kotlin.

If I’m not mistaken, generics in Kotlin are not reified, as in Java. So a catch block couldn’t distinguish, at runtime, between an exception that uses a type parameter A from the same exception using B. However, that isn’t important for me. What I want is to have a way of retrieving data from the exception, which is type-safe. As with regular classes. Nothing else.

When me and others have commented this feature to other Java developers, some of them have argued that it would cause clashes in the catch clause, because of the type erasure. However, overriding methods that use generics also causes the same clashes, but the designers of Java don’t stop us from using generics in methods. The compiler and the IDE report us about that problem and we deal with it. Is the opinion of the Kotlin designers similar to this? Or is there a possibility of changing their minds?

Thank you for your help.


#2

Can you provide a code example showing what you’re trying to achieve?


#3

Yole, thank you for your prompt answer.

I’ll try to explain it with a simplified example. I hope that this simplification doesn’t distort the real problem. Let’s try…

I’ve a class that retrieves registries from a database. To find these registries, it can use different types of primary keys. This is what I do in Java:

class DatabaseManager {
  public <T extends Registry> T find (Object primaryKey)
      throws RegistryNotFoundException {
      }
}

The RegistryNotFoundException exception is thrown if the primary key doesn’t exist in the database. I store the primary key in the exception itself, so that it can be handled adequately in an upper layer. For example, that layer could format the key in different ways depending on its type, to show it to the user.

In Java, I would like being able to do this:

class DatabaseManager {
  public <K, V extends Registry> V find (K primaryKey)
      throws RegistryNotFoundException<K> {
      }
}
class StudentEjb {
  public void saveScore (Long studentPk, String examPk,
      Integer difficultyPk, Integer variationPk, Short score) {}
      throws RegistryNotFoundException<Integer>,
          RegistryNotFoundException<Long>,
          RegistryNotFoundException<String> {

    // Code...
    Student student = databaseManager.find (studentPk);
    Exan exam = databaseManager.find (examPk);
    // More code...

  }
}
class StudentServlet {

  @Override
  protected void doGet (HttpServletRequest request,
      HttpServletResponse response)
      throws ServletException, IOException {

    // Code...
    try {

      studentEjb.saveScore (studentPk, examPk,
          difficultyPk, variationPk, score);

    } catch (RegistryNotFoundException<Integer> ex) {
      // Specific handling for Integers...
    } catch (RegistryNotFoundException<Long> ex) {
      // Specific handling for Longs...
    } catch (RegistryNotFoundException<String> ex) {
      // Specific habnling for Strings...
    }

  }

}

Obviously, this is not possible in Java, because generics are not reified… Neither in Kotlin, I guess. So I’ve just realized that it hasn’t any sense to implement generics in exceptions, as long as they’re not reified. Because there wouldn’t be a way to differentiate between them at runtime.

Anyway, I’ve continued thinking about this issue, and I’ve found out that, if Kotlin finally implemented disjoint unions, one could implement the exception this other way:

typealias PrimaryKey = Integer | Long | String;

class RegistryNotFoundException (val primaryKey: PrimaryKey)
    : Exception ("Blah, blah");

And then, do something like this:


  typealias PrimaryKey = Integer | Long | String;

  protected fun doGet (request: HttpServletRequest,
      response: HttpServletResponse) {

    try {

      studentEjb.saveScore (studentPk, examPk,
          difficultyPk, variationPk, score);

    } catch (ex: RegistryNotFoundException) {

      val pk: PrimaryKey = ex.getPrimaryKey ();
      val msg: String = when (pk) {
        is Integer ->// Specific handling for Integers...
        is Long -> // Specific handling for Longs...
        is String -> // Specific handling for Strings...
      }
      printMessage (msg);

    }

  }

In case that we forgot to handle one of the types in the where expression, the compiler could throw an error, as it does with sealed classes, Right? If that’s true, then it would be a very decent alternative to generics in this specific case.

I know that this wouldn’t be as flexible as having true generics in exceptions. But, since we usually know beforehand which types are accepted by our methods, this feature would cover most cases.

What do you opine?


#4

Why don’t you just use sealed classes?

sealed class PrimaryKey

class IntPrimaryKey(val value: Int): PrimaryKey
class LongPrimaryKey(val value: Long): PrimaryKey

class RegistryNotFoundException(val primaryKey: PrimaryKey)

#5

I’ve also considered that idea. But I’ve thought that having to wrap each primary key with one instance of a sealed class, could make the use of my API more verbose. It’s not something severe, I know. But it has caused me some doubts.

From the point of view of performance, I guess that the JVM could optimize this code well, after some in-lining. Maybe even removing the instantiation of the wrapper all together. So I guess that this is not a problem.

I think I’ll follow your recommendation :wink: . But I would love that you considered the addition of disjoint unions.

Thank you for your help!