Java enums will be enhanced with generics support and with the ability to add methods to individual items, a new JEP shows. Since both features can be delivered with the same code change, they are bundled together in the same JEP. The change only affects the Java compiler, and therefore no runtime changes are needed. Although there is no target version, Java 10 seems likely.
The change didn't initially get a postive reaction, with prominent Java Champions like Joshua Bloch questioning its usefulness. However, some further discussion and the presentation of new uses cases helped it gain support.
In case you didn't see my resp to @BrianGoetz, I now see the point of the JEP and withdraw my objections. Use case:https://t.co/O1tJO8oSCp
— Joshua Bloch (@joshbloch) December 7, 2016
Let's now show what the changes would allow the developer to do by summarising some of the use cases presented in the JEP and other discussions. Java Champion Lukas Eder presented a StackOverflow question that introduced a case for retrieving and setting properties from a config file, web session, or similar, in a type-safe manner. An enum with generics support would allow us to indicate the set of available keys together with their associated type:
public enum Key<T> {
HOST<String>,
PORT<Integer>,
SCORE<Double>
}
public interface PropertiesStore {
public <T> void put(Key<T> key, T value);
public <T> T get(Key<T> key);
}
Now these keys can be safely retrieved and stored from and into the properties store, since expressions like the following will fail to compile:
put(PORT, “not a number”); // error, type mismatch: PORT is Key<Integer>
// “not a number” is String
On the other hand, allowing individual items to have their own methods can help define operations that apply only to certain properties. Under JEP 301, the above definition could be expanded as follows:
public enum Key<T> {
HOST<String>,
PORT<Integer>,
SCORE<Double> {
double normalise(double x) {
// score normalisation logic
return result;
}
}
}
With the current enums, all the items would have the generic type Key
, which means the method normalise
wouldn't be visible. However, after this work is finished, the compiler would preserve this kind of information, which means the following would be true:
SCORE.normalise(5.37); // compiles
HOST.normalise(5.37); // error: neither HOST nor Key have normalise
The way to accomplish this will be by changing how the exposed static type of the individual items of the enum is calculated. As the reader may know, enums were added in Java 5 as mere syntactic sugar: the JVM doesn't have any special treatment for enums, instead, the compiler translates an enum into a normal class with static objects, and then compiles this into bytecode. Putting aside some technical aspects, the following enum:
public enum Colour {
RED, GREEN, BLUE
}
Is roughly translated by the compiler to the following (this isn't an accurate representation, but it's close enough for the purpose of this explanation):
public class Colour extends Enum {
public static final Colour RED = new Colour();
public static final Colour GREEN = new Colour();
public static final Colour BLUE = new Colour();
private Colour() {}
}
Since the type of all the items is Colour
, any item-specific method or type information is lost. What JEP 301 will do is identify the cases where using the generic enum type is not enough to represent the individual items, and in such cases generate further, more specific types like Colour$RED
, Colour$GREEN
and Colour$BLUE
.
Enhanced enums may benefit even further from other work being currently carried out in other parts of the JDK. On one side, local variable type inference might allow developers to get hold of the sharper types created by the compiler even though the exact form of these is unknown at the time of writing code; this would mean that, considering the code above, the following would be possible:
var s = Key.SCORE; // type of s derived as Key$SCORE
s.normalise(8.29); // method normalise can be accessed
On the other side, anecdotical evidence and sample use cases suggest that generics in enums might be extensively used with primitive types, which is rather inefficient (the JVM has to use the boxed counterpart for each primitive type). This could be improved with the part of Project Valhalla that deals with Generics over Primitive Types, removing this barrier and making it available for mass utilisation.
Update: About Sharper Types
Some readers have expressed confusion about the term "sharper type", used both in the original JEP and in this article, adducing that it doesn't correspond to any existing technical term in Java parlance. In this context, the expression "sharper type" is being used to refer to a type that extends another one, and therefore is more specific or "sharper". In the example of Key
above, the enum would roughly be translated after compilation to class Key extends Enum
, but the type of SCORE
would be something like class Key$SCORE extends Key
so as to be able to access the method normalise
. In this case we say that the type Key$SCORE
is sharper than Key
because, while the latter can be applied to all the elements in the enum, the former can only be applied to one of them.