Can someone please explain type inference to me? Out-of-the-box database notifications: JMX and Derby in Java 6
Oct 01

I’ve been working with GWT (Google’s Web Toolkit) a bit recently, and I must first say it’s an amazing tool. It does have some downfalls, like requiring you to use J2SE 1.4 syntax, so no enum or generics when developing with GWT.. That said, there’s nothing stopping you from using the Java SE 5 or 6 framework, so concurrency and all the other goodies are still there (to be used on the server side, obviously).

And that’s just unfortunate, because I really got used to using enums! Still, not having the syntax is not going to stop me. After all, enums in Java are eventually translated into classes, so I can just write my own GWT-compatible Enum-class.

I started with the following base:

public class Enum implements IsSerializable {
  private final String name;
  private final int ordinal;

  protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

public final class ExampleEnum extends Enum {
  private ExampleEnum(String name, int ordinal) {
    super(name, ordinal);
  }

  public static final ExampleEnum example1 =
    new ExampleEnum("example1", 0);
  public static final ExampleEnum example2 =
    new ExampleEnum("example2", 1);
}

However, that didn’t work, as there are some restrictions in how GWT translates Java code to web browser code. First of all, it didn’t pass compilation because GWT requires that a serializable class have a no-args, public constructor. This is unfortunate, but if GWT requires, so be it. Second, even after doing that the classes wouldn’t go through serialization properly, and after a short examination it appeared that final fields are the same as transient fields when it comes to serialization, which meant that the name and ordinal fields weren’t being deserialized. So, I’ve made the changes needed, added a couple of getters, and got the following:

public class Enum implements IsSerializable {
    private /* final */ String name;
    private /* final */ int ordinal;

    public Enum() {
        this("error", -1);
    }

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    public final String getName() {
        return name;
    }

    public final int getOrdinal() {
        return ordinal;
    }
}

Now I wanted to add the much needed hashCode and equals methods. The hashCode method was simple: return getOrdinal(). The user had to be trusted to use a unique number for each value. The equals method proved harder, though: Usually during an equality check, the type of the class is compared. However, since class type is not saved (more specifically, the Object.getClass method doesn’t go through GWT-compilation), a simple getClass() == o.getClass() doesn’t work here. In order to overcome this, GWT offers a utility method, GWT.getTypeName, which returns the name of the class. This could have been great, however this method doesn’t work on the server side, which makes it impossible to implement any type checking at all. I could have chosen to pass another parameter at the constructor, asking for the type name – But eventually decided against it. Instead, I tried to bring the chance of clash to a minimum, and since it’s for personal use, I know I won’t use it badly.

(If you’re interested in why the GWT.getTypeName doesn’t work: GWT uses the keyword native to write native JavaScript code. The GWT utility class’ methods are all marked native, since they’re all supposed to be used on the client-side. However, when this is run on the server-side, the real Java VM is running there, and it’s looking for a JNI library, which is obviously nowhere to be found – and therefore, a UnsatisfiedLinkError is thrown.)

I’ve also added a little mechanism to Enum to make it easier to implement a valueOf method easier. This brings the final code to the following:


public class Enum implements IsSerializable {
    private /* final */ String name;
    private /* final */ int ordinal;

    public Enum() {
        this("error", -1);
    }

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    public final String getName() {
        return name;
    }

    public final int getOrdinal() {
        return ordinal;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;

        Enum anEnum = (Enum) o;

        if (!name.equals(anEnum.name) || ordinal != anEnum.ordinal)
          return false;

        return true;
    }

    public int hashCode() {
        return ordinal;
    }

    protected static Map makeValuesMap(Enum[] enums) {
        Map result = new HashMap(enums.length);
        for (int i = 0; i < enums.length; i++) {
            Enum anEnum = enums[i];
            result.put(anEnum.getName(), anEnum);
        }

        return result;
    }
}

public class ExampleEnum extends Enum {
  public ExampleEnum() { }

  private ExampleEnum(String name, int ordinal) {
    super(name, ordinal);
  }

  public static /* final */ ExampleEnum example1 =
    new ExampleEnum("example1", 0);
  public static /* final */ ExampleEnum example2 =
    new ExampleEnum("example2", 1);

  private static /* final */ Map values =
    makeValuesMap(new Enum[] { example1, example2 });

  public static ExampleEnum valueOf(String name) {
    return values.get(name);
  }
}

The biggest shame is that even with this admittedly-quite-cool-code, I still can’t use the EnumMap and EnumSet classes, which are great help and are very efficient when it comes to enums.

Update: With GWT 1.5, Java enums are supported and can be used freely!

  • Share/Bookmark

8 Responses to “Enums & GWT”

  1. Kris Says:

    Hi,
    If I create an Eclipse project for GWT it creates a package for server code. How can I just compile the server package with 1.5 option? Or is there a better way to structure your project so you can leverage 1.5+ features on the server side code.

    Thanks.

  2. Avah Says:

    Kris: Maybe I was misunderstood. I didn’t mean I could compile with 1.5 options – I just compiled the server code against the JDK 1.6 (actually, the entire project – but obviously only the server code is being compiled using javac).

    Basically, just point your dependency libraries (or classpath) to the JDK 1.6’s library. Then you can use stuff like java.util.concurrency etc.

  3. Mohammad Says:

    Can I post this code snippet to Wikicodia?

  4. Avah Says:

    For clarity’s sake: In private exchange of letters, I’ve approved it – and the code snippet can be found here.

    Thanks Mohammad!

  5. Mark Says:

    You can feel free to use any level of JDK to compile, along with those options, when compiling your server code. GWT does its own compiling directly from source, and so as long as you keep track of what’s client (and server) and what’s ONLY server, you’re fine.

    For example, my servlets and server code all use Generics, for-each loops, etc. I just make sure I don’t use any of that stuff in Eclipse. It takes some getting used to (for example, my Eclipse project is set against 1.5, so not everything that compiles in the project would compile under GWT), but it’s worth having stuff like Generics on the server side.

    When it comes to anything serialized over RPC, though, you’re stuck with 1.4 on both the client and the server, as you’ve said. Clever solution anyway, and hopefully GWT 1.5 will come out soon (which includes J2SE 1.5 support)

  6. Mark Says:

    Sorry, to correct that last post, I meant to say “I don’t use any of that stuff in client-side code,” not “I don’t use any of that stuff in Eclipse”.

  7. Adrian Smith Says:

    FYI: This has slightly different semantics from the Java 5 enums, as the Java enums are all singletons, and thus can be compared by identity, and don’t need to use the equals method.

    http://www.databasesandlife.com/java-5-enums-can-be-compared-with/

  8. Chaotic Java » Blog Archive » Enums, Generics and for-each loops in GWT 1.5 Says:

    [...] the set of Java language features added to Java 5 by adding support for enums (much better than my workaround enum), generics and for-each loops. GWT itself even uses generics for the asynchronous calls, so that [...]

Leave a Reply

Chaotic Java is Digg proof thanks to caching by WP Super Cache