Proposal: Add a type token class for representing generic types

Gunnar Morling gunnar at hibernate.org
Fri Dec 23 08:08:20 UTC 2016


Hi,

I've sent this proposal to core-libs-dev recently and it has been
suggested that it may fall into the scope of project Valhalla, hence
I'm proposing it here, too. Any feedback would be very welcome.

Thanks,

--Gunnar

=====
I'd like to suggest the addition of a type token class to the Java
class library, to be used for representing generic types such as
List<String>.

Actual class literals can only represent raw types. But often
libraries have the need to apply some sort of configuration to
specific generic types, link specific behaviour to them or expose
(meta) data related to them. The suggested type token class would
allow to implement such cases in a type-safe fashion.

# Example use cases

* The "type-safe heterogenous container" pattern from Joshua Bloch's
book "Effective Java" introduces a container which allows to safely
store and retrieve objects of different types. They are keyed by
Class<?>, which means that one cannot have a value for a List<String>
and another value for a List<Integer> in such container. Also one
cannot obtain a List<String> without casting from such container. Type
literals would allow this:

    <T> void put(TypeLiteral<T> type, T value);
    <T> T get(TypeLiteral<T> type);

    TypeLiteral<List<String>> stringListType = ...;
    List<String> stringList = container.get( stringListType );

* JAX-RS allows to read response message entities into parameterized
types using its GenericType class:

    List<Customer> customers = response.readEntity( new
GenericType<List<Customer>>() {} );

* CDI allows to dynamically obtain beans of specific parameterized
types using its TypeLiteral class:

    @Inject @Any Instance<PaymentProcessor> anyPaymentProcessor;

    public PaymentProcessor<Cheque> getChequePaymentProcessor() {
        PaymentProcessor<Cheque> pp = anyPaymentProcessor
            .select( new TypeLiteral<PaymentProcessor<Cheque>>() {} ).get();
    }

# Proposed solution

Type literals would be represented by a new type
java.lang.reflect.TypeLiteral<T>. Instances would be created via
sub-classing, capturing the <T> parameter and baking it into the
sub-class:

    TypeLiteral<List<String>> stringListType = new
TypeLiteral<List<String>>(){};

That'd allow to provide type-safe APIs around generic types as shown
in the examples above. The following methods should be defined on
TypeLiteral in order to make it useful for implementers of such APIs:

    Type getType();
    Class<?> getRawType();
    boolean equals(Object obj);
    int hashCode();
    String toString();

# Prior art

The idea of type tokens based on capturing a generic type in a
sub-class has been around for a long time. The first reference I've
found is Neal Gafter's blog post on "Super Type Tokens" from 2006 [1].
Examples in real-world APIs and libraries are TypeLiteral<T> in CDI
[2], GenericType<T> in JAX-RS [3], TypeLiteral<T> in commons-lang [4]
and TypeToken<T> in Guava [5].

# Alternatives and shortcomings

The obvious alternative would be reified generics, but I think it's
commonly agreed upon that these will not come to the Java language any
time soon. Without native support in the language itself the proposed
type literal class is the best way to support the mentioned use cases
as far as I can say.

Shortcomings are the creation of a sub-class per literal instantiation
(probably more an aesthetic problem, though) and failure of the
pattern when type variables are included anywhere in the represented
type [6]. Unfortunately, this can only be detected at runtime when
instantiating a literal but not by the compiler (although, I reckon it
technically could, by handling this case specifically).

# Conclusion

Lacking reified generics, the Java platform would benefit from the
addition of new type token class java.lang.reflect.TypeLiteral. The
pattern's presence in common APIs and libraries shows that there is a
wide-spread need for such mechanism and the community would benefit
very much from a standardized solution in the JDK itself. This will
allow for more unified API designs as well as foster integration of
different libraries.

I'm eager to learn about your feedback and the OpenJDK team's
assessment of this proposal.

--Gunnar

[1] http://gafter.blogspot.de/2006/12/super-type-tokens.html
[2] https://docs.oracle.com/javaee/7/api/index.html?javax/enterprise/util/TypeLiteral.html
[3] https://docs.oracle.com/javaee/7/api/index.html?javax/ws/rs/core/GenericType.html
[4] https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeLiteral.html
[5] https://google.github.io/guava/releases/20.0/api/docs/index.html?com/google/common/reflect/TypeToken.html
[6] http://gafter.blogspot.de/2007/05/limitation-of-super-type-tokens.html


More information about the valhalla-dev mailing list