Type Literals |
Because DME Component Libraries was originally developed for the .NET Framework, some subsystems require runtime use of the types of generic parameters. However, since Java implements its generics using type erasure, this information is not available from the JVM itself, so this information must be stored in your application code directly.
One common approach to this problem is to use Class objects to store this information. For example:
public static <T> boolean isT(Object obj, Class<T> klass) { return klass.isInstance(obj); }
Object obj = "a string"; boolean isString = isT(obj, String.class); // => true boolean isInteger = isT(obj, Integer.class); // => false
However, this approach has some limitations. Class literals can only be created for raw types, that is, it is impossible to write Map<String, List<String>>.class, or List<T>.class. To avoid this, DME Component Libraries uses special objects called TypeLiterals to capture information about the full type at the point where it is specified, based on a technique described by Neal Gafter, and an implementation by Gunni Rode.
How does it work? Consider this example:
public static <T> boolean isT(Object obj, TypeLiteral<T> typeLiteral) { // for simplicity this only checks the raw type. More complicated // techniques can be used to check the full type. return typeLiteral.asClass().isInstance(obj); }
Object obj = "a string"; boolean isString = isT(obj, new TypeLiteral<String>() {}); // => true boolean isMapOfIntegerToDouble = isT(obj, new TypeLiteral<Map<Integer, Double>>() {}); // => false
To construct a TypeLiteral, you simply construct an anonymous inner subclass of TypeLiteral, parameterized on the type you need to capture.
The following example uses TypeLiteral in the context of DME Component Libraries, specifically with the ScalarBitErrorRate type from the Communications Library.
Scalar scalarBpsk = new ScalarBitErrorRate<>(new TypeLiteral<ModulationBpsk>() {}); Scalar scalarBpsk2 = new ScalarBitErrorRate<>(new TypeLiteral<ModulationBpsk>() {}); Scalar scalarQpsk = new ScalarBitErrorRate<>(new TypeLiteral<ModulationQpsk>() {}); boolean result1 = scalarBpsk.isSameDefinition(scalarBpsk2); // => true boolean result2 = scalarBpsk.isSameDefinition(scalarQpsk); // => false