Switch on java.lang.Class

Tagir Valeev amaembo at gmail.com
Mon Apr 9 05:07:05 UTC 2018


Hello!

I don't remember whether switch on java.lang.Class instance was discussed.
I guess, this pattern is quite common and it will be useful to support it.
Such code often appears in deserialization logic when we branch on desired
type to deserialize. Here are a couple of examples from opensource
libraries:

1. com.google.gson.DefaultDateTypeAdapter#read (gson-2.8.2):

    Date date = deserializeToDate(in.nextString());
    if (dateType == Date.class) {
      return date;
    } else if (dateType == Timestamp.class) {
      return new Timestamp(date.getTime());
    } else if (dateType == java.sql.Date.class) {
      return new java.sql.Date(date.getTime());
    } else {
      // This must never happen: dateType is guarded in the primary
constructor
      throw new AssertionError();
    }

Could be rewritten as:

    Date date = deserializeToDate(in.nextString());
    return switch(dateType) {
      case Date.class -> date;
      case Timestamp.class -> new Timestamp(date.getTime());
      case java.sql.Date.class -> new java.sql.Date(date.getTime());
      default ->
        // This must never happen: dateType is guarded in the primary
constructor
        throw new AssertionError();
    };

2.
com.fasterxml.jackson.databind.deser.std.FromStringDeserializer#findDeserializer
(jackson-databind-2.9.4):

    public static Std findDeserializer(Class<?> rawType)
    {
        int kind = 0;
        if (rawType == File.class) {
            kind = Std.STD_FILE;
        } else if (rawType == URL.class) {
            kind = Std.STD_URL;
        } else if (rawType == URI.class) {
            kind = Std.STD_URI;
        } else if (rawType == Class.class) {
            kind = Std.STD_CLASS;
        } else if (rawType == JavaType.class) {
            kind = Std.STD_JAVA_TYPE;
        } else if // more branches like this
        } else {
            return null;
        }
        return new Std(rawType, kind);
    }

Could be rewritten as:

    public static Std findDeserializer(Class<?> rawType)
    {
        int kind = switch(rawType) {
        case File.class -> Std.STD_FILE;
        case URL.class -> Std.STD_URL;
        case URI.class -> Std.STD_URI;
        case Class.cass -> Std.STD_CLASS;
        case JavaType.class -> Std.STD_JAVA_TYPE;
        ...
        default -> 0;
        };
        return kind == 0 ? null : new Std(rawType, kind);
    }

In such code all branches are mutually exclusive. The bootstrap method can
generate a lookupswitch based on Class.hashCode, then equals checks, pretty
similar to String switch implementation. Unlike String hash codes
Class.hashCode is not stable and varies between JVM launches, but they are
already known during the bootstrap and we can trust them during the VM
lifetime, so we can generate a lookupswitch. The minor problematic point is
to support primitive classes like int.class. This cannot be passed directly
as indy static argument, but this can be solved with condy.

What do you think?

With best regards,
Tagir Valeev.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20180409/d3e2cc1b/attachment.html>


More information about the amber-spec-experts mailing list