Using Types

Type Objects

A Type object can represent any type that we might be able to assign to a variable in Java. Some examples:

  • java.lang.String>
  • java.util.Collection<?>
  • java.util.List<java.lang.String>
  • java.util.ArrayList<? extends java.io.Serializable>
  • java.util.HashMap<java.lang.Integer, ? super java.util.List<java.lang.Float[]>>

javaRuntype's Type objects are also serializable, and javaRuntype's engine keeps the number of Type instances low, so that only one object representing a specific Java type exists in memory from a specific classloader at a moment. This allows a comfortable and easy use of big amounts of type objects without worrying too much for memory efficiency.

All Type objects created with javaRuntype are valid, as the type engine will not allow an invalid type (like "List<String,Integer>") to be created. An exception will be raised if the user tries to obtain a Type object for a java type which is not valid for the current classloader javaRuntype is running in.

Type API

Type<T> objects offer a series of methods:

MethodReturn typeDescription
getName()StringReturns the type's name. For instance, "java.util.List<java.lang.String>".
getSimpleName()StringReturns the type's name excluding package. For instance, "List<java.lang.String>".
isAbstract()booleanReturns whether the type is abstract or not. For instance, it will return "false" for "AbstractList<String>[]" as, although AbstractList.class is abstract, AbstractList[] is an instantiable class. On the contrary, "AbstractList<String>" would return "true".
isArray()booleanReturns whether the type is an array or not. For instance, it will return "true" for "List<String>[]".
isAssignableFrom(Type<?> type)booleanReturns whether type type is assignable from the type that is passed as a parameter. Assignability is computed in the following terms: a type A is considered to be assignable from another type B if a method declared as receiving a parameter of type A can be called with an object of type B as a parameter.
isInterface()booleanReturns whether the type represents an interface or not. For instance, it will return "false" for "List<String>[]" as, although List.class is an interface, List[].class is a instantiable class. On the contrary, "List<String>" would return "true".
isRaw()booleanReturns whether this type would be considered "raw". A type is raw if the result of calling getRawEquivalent() on it is the type itself.
getRawClass()Class<? super T>Returns the type's raw equivalent class representation. For instance, if the type is "List<String>[]", this method will return List[].class (the class representing the raw version of the type), in contrast with getComponentClass() which would return List.class (the component class).
getComponentClass()Class<?>Returns the type's component class. For instance, it will return List.class for "List<String>[]" (and not List[].class, which would be returned by getRawClass()).
getArrayDimensions()intReturns the type's array dimensions. For instance, it will return 1 for "List<String>[]"
getTypeParameters()List<TypeParameter<?>>Returns the type parameters of the type.
getAllTypesAssignableFromThis()Set<Type<?>>Returns a set of the types corresponding to all the interfaces and superclasses that this type implements or extends. For instance, for "List<String>", this method will return "Collection<String>", "Iterable<String>" and "Object".
getRawEquivalent()Type<?>Returns a type corresponding with the one on which this method is called, but substituting all its type parameters by "unknown". For instance: "List<String>" -> "List<?>".
newInstance()ObjectCreates a new instance of the type. The returned instances are created using the default (no-arg) constructor if this Type does not represent an array. On the contrary, if this Type represents an array, an array object of zero-size dimensions is returned (like "String[0][0][0]").

Obtaining Type objects

Static constants

The Types (org.javaruntype.type.Types) class offers a comprehensive set of static constants for some of the most used types, for example:

Java TypeConstant
java.lang.StringTypes.STRING
java.lang.IntegerTypes.INTEGER
java.util.CalendarTypes.CALENDAR
java.io.SerializableTypes.SERIALIZABLE
java.lang.Long[]Types.ARRAY_OF_LONG
java.util.List<?>Types.LIST_OF_UNKNOWN
java.util.List<java.lang.String>Types.LIST_OF_STRING
java.util.Set<java.util.Calendar>Types.SET_OF_CALENDAR
java.util.Map<?,?>Types.MAP_OF_UNKNOWN_UNKNOWN
java.util.Map<java.lang.String,java.lang.String>Types.MAP_OF_STRING_STRING
java.util.Map<java.lang.String,java.lang.Boolean>Types.MAP_OF_STRING_BOOLEAN

...and many more...

From class

Raw type from class
    Type<String> strType = Types.forClass(String.class);    
            
    ...
    
    Type<List<?>> listOfUnkType = Types.forClass(List.class);    
Parameterized type from class
    Type<List<String>> listOfStrType = 
        Types.forClass(
            List.class,
            TypeParameters.forType(Types.STRING));
            
    ...
    
    Type<Map<String,? extends Number>> mapOfStrExtNumberType = 
        Types.forClass(
            Map.class,
            TypeParameters.forType(Types.STRING),
            TypeParameters.forExtendsType(Types.NUMBER));

From name

This method of obtaining a Type object requires a cast:

    Type<String> strType = (Type<String>) Types.forName("String");
    
    ...
    
    Type<Map<String,? extends Number>> mapOfStrExtNumberType = 
        (Type<Map<String,? extends Number>>) Types.forName("Map<String,? extends Number>");

When specifying a type by name, classes in the java.lang, java.util, java.math and java.io packages do not need their packages to be specified (as you can see in the above examples).

But:

    Type<URL> urlType = (Type<URL>) Types.forName("java.net.URL");

From java.lang.reflect.Type objects

javaRuntype's types can be obtained from java.lang.reflect.Type objects as such obtained from methods like java.lang.reflect.Method#getGenericReturnType().

    
    Method aMethod = SomeClassOfMine.class.getMethod("toString");
    java.lang.reflect.Type javaLangReflectType = aMethod.getGenericReturnType();
    
    Type<String> strType = (Type<String>) Types.forJavaLangReflectType(javaLangReflectType);
    

When obtaining Types this way, variables present in types can be substituted. For example, if you had a getData() method defined like:

    public class DataClass {
        ...
        public <E> Map<String,List<E>> getData() {
            ...
        }
        ...
    }
    

You could obtain the java.lang.reflect.Type object for the return type with:

    
    Method getDataMethod = DataClass.class.getMethod("getData");
    java.lang.reflect.Type javaLangReflectType = getDataMethod.getGenericReturnType();
    

But, as javaRuntype's Type objects cannot contain unresolved variables (like E in Map<String,List<E>>), in order to obtain a type we will have to resolve it by creating a variables map linking variable names to Types:

    Map<String,Type<?>> variables = new HashMap<String, Type<?>>();
    variables.put("E", Types.INTEGER);
    

...and then use it to obtain our javaRuntype Type, knowing that "E" is Integer:

    // type will be "Map<String,List<Integer>>"
    Type<?> type = Types.forJavaLangReflectType(javaLangReflectType, variables);

...or using a cast, if we prefer to be exact:

  
    Type<Map<String,List<Integer>>> type = 
        (Type<Map<String,List<Integer>>>) Types.forJavaLangReflectType(javaLangReflectType, variables);
  

Parameterized types from parameters

The Types class can easily create some common parameterized Types from its Type parameters. For example:

   Type<String[]> strArrType = Types.arrayOf(Types.STRING);

Available utility methods are:

TransformationFactory Method
T -> T[]Types.arrayOf(Type<T>)
T -> Iterable<T>Types.iterableOf(Type<T>) Types.iterableOf(TypeParameter<T>)
T -> Class<T>Types.classOf(Type<T>) Types.classOf(TypeParameter<T>)
T -> Collection<T>Types.collectionOf(Type<T>) Types.collectionOf(TypeParameter<T>)
T -> Comparator<T>Types.comparatorOf(Type<T>) Types.comparatorOf(TypeParameter<T>)
T -> Enumeration<T>Types.enumerationOf(Type<T>) Types.enumerationOf(TypeParameter<T>)
T -> Iterator<T>Types.iteratorOf(Type<T>) Types.iteratorOf(TypeParameter<T>)
T -> List<T>Types.listOf(Type<T>) Types.listOf(TypeParameter<T>)
T -> ListIterator<T>Types.listIteratorOf(Type<T>) Types.listIteratorOf(TypeParameter<T>)
K,V -> Map<K,V>Types.mapOf(Type<K>,Type<V>) Types.mapOf(TypeParameter<K>,TypeParameter<V>)
K,V -> Map.Entry<K,V>Types.mapEntryOf(Type<K>,Type<V>) Types.mapEntryOf(TypeParameter<K>,TypeParameter<V>)
T -> Queue<T>Types.queueOf(Type<T>) Types.queueOf(TypeParameter<T>)
T -> Set<T>Types.setOf(Type<T>) Types.setOf(TypeParameter<T>)

Component types out of parameterized types

The Types class can extract types from parameterized types, like:

   Type<String> strType = Types.arrayComponentOf(Types.ARRAY_OF_STRING);

Available utility methods are:

TransformationFactory Method
T[] -> TTypes.arrayComponentOf(Type<T[]>)
Iterable<T> -> TTypes.iterableComponentOf(Type<Iterable<T>>)
Class<T> -> TTypes.classComponentOf(Type<Class<T>>)
Collection<T> -> TTypes.collectionComponentOf(Type<Collection<T>>)
Comparator<T> -> TTypes.comparatorComponentOf(Type<Comparator<T>>)
Enumeration<T> -> TTypes.enumerationComponentOf(Type<Enumeration<T>>)
Iterator<T> -> TTypes.iteratorComponentOf(Type<Iterator<T>>)
List<T> -> TTypes.listComponentOf(Type<List<T>>)
ListIterator<T> -> TTypes.listIteratorComponentOf(Type<ListIterator<T>>)
Map<K,V> -> KTypes.mapKeyComponentOf(Type<Map<K,V>>)
Map<K,V> -> VTypes.mapValueComponentOf(Type<Map<K,V>>)
Map.Entry<K,V> -> KTypes.mapEntryKeyComponentOf(Type<Map.Entry<K,V>>)
Map.Entry<K,V> -> VTypes.mapEntryValueComponentOf(Type<Map.Entry<K,V>>)
Queue<T> -> TTypes.queueComponentOf(Type<Queue<T>>)
Set<T> -> TTypes.setComponentOf(Type<Set<T>>)