Interface TypeMap<V>

Type Parameters:
V - the type of the values in the map The type of the values in the Map
All Superinterfaces:
Map<Class<?>,V>

public sealed interface TypeMap<V> extends Map<Class<?>,V>

A specialisation of the Map interface, aimed at providing natural default values for groups of Java types via a shared supertype. The keys in a type map are always Class objects. The type of the values is user-defined. One particularly useful application of type maps is to associate types with functions (i.e. lambdas, method references) that operate on those types.

Behaviour

A TypeMap behaves as follows: if the type requested via get() is not present in the map, but one of its supertypes is, then the type map will return the value associated with the supertype. Similarly, Map.containsKey(Object) containsKey()} will return true if either the specified type itself or one of its supertypes is a key in the TypeMap. For example: if the user requests the value associated with key Integer.class, but the map only contains an entry for Number.class, then the value associated with Number.class is returned. If there is no entry for Number.class either, but there is one for Serializable.class, then the value associated with that type is returned.

Regular types take precedence over interface types. If the requested type is an implementation of one type within the type map, and a subclass of another, then the value associated with the latter will be returned. The one exception is Object.class, since any instance of any interface is also an Object. If Object.class is present in the map, the get() method is guaranteed to return a non-null value. Note that this is, in fact, a deviation from Java's type hierarchy since primitive types do not extend Object.class. However, the point of the TypeMap interface is to provide natural default values for groups of types, and Object.class is the obvious candidate for providing the ultimate, last-resort, fall-back value.

Type maps are immutable. All map-altering methods throw an UnsupportedOperationException. The getOrDefault() method also throws an UnsupportedOperationException as it sidesteps the TypeMap paradigm. Type maps are also null-repellent — neither keys nor values are allowed to be null.

You cannot instantiate a TypeMap directly. You obtain an instance through the various static factory methods on the TypeMap interface itself.

Autoboxing

A TypeMap can be configured to "autobox" the types requested via get() and containsKey(). For example, if the user makes a request for double.class, but the map only contains an entry for Double.class, then the value associated with Double.class is returned. If there is no entry for Double.class either, but there is one for Number.class, then the value associated with Number.class is returned. Thus, with autoboxing enabled, you need (and should) only add the wrapper types to the map, unless you want the primitive type to be associated with a different value than the wrapper type. This applies not just to primitive types, but also to arrays of a primitive type. Thus, with autoboxing enabled, int[] will be "autoboxed" to Integer[]. Note that, irrespective of whether autoboxing is enabled or not, the presence of Object.class in the map always guarantees that a non-null value will be returned for whatever type is requested, even if it is a primitive type. Autoboxing is enabled by default.

Auto-expansion

Even though type maps are specified to be immutable to the outside world, the "greedy" TypeMap will automatically and tacitly absorb subtypes of the original types in the map, as and when they are requested via get() or containsKey(). It will look up the value for the nearest supertype and associate the subtype with that same value. Thus, the next time the subtype is requested, it will result in a direct hit. Note that an auto-expanding type map is still immutable to the outside world and that the map will still only ever contain subtypes of the types with which the map was seeded. No new branches of the Java type hierarchy will emerge - unless, of course, the original map contained Object.class.

Implementations

Through its static factory methods and Builder classes, the TypeMap interface provides access to four different implementations. Which implementation to choose strongly depends on the internal makeup of the map (the interdependencies between the types within the map), the size of the map, and the ratio between the size of the map and the total number of types it is going to be queried for. For high ratios (a small map processing a large variety of types), the "type graph" is most likely the best choice, especially if the types in the map tend to be base types (like Number and CharSequence) while the types requested from it are concrete types (like Integer and String). Otherwise any of the other implementations will do, and you will have to test which implementation performs best.

Author:
Ayco Holleman
  • Field Details

  • Method Details

    • fixedTypeMap

      static <V> TypeMap<V> fixedTypeMap(Map<Class<?>,V> m)
      Returns a TypeMap that is internally backed by a regular, unmodifiable Map. Autoboxing is enabled in the returned TypeMap.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      Returns:
      a TypeMap that performs reliably well in many cases
    • fixedTypeMap

      static <V> TypeMap<V> fixedTypeMap(Map<Class<?>,V> m, boolean autobox)
      Returns a TypeMap that is internally backed by a regular, unmodifiable Map.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      autobox - whether to "autobox" the types requested via get() and containsKey()
      Returns:
      a TypeMap that performs reliably well in many cases
    • fixedTypeMapBuilder

      static <V> TypeMapBuilder<V> fixedTypeMapBuilder()
      Returns a Builder for "fixed" type maps.
      Type Parameters:
      V - the type of the values in the map
      Returns:
      a Builder for "fixed" type maps
    • nativeTypeMap

      static <V> TypeMap<V> nativeTypeMap(Map<Class<?>,V> m)
      Returns a TypeMap that directly implements the Map interface rather than being backed by a regular map. Instead, it relies on a data structure similar to the Java type hierarchy itself. This implementation is sensitive to the insertion order of the types. Thus, if you expect a lot of requests for, say, String.class, it pays to initialize it with a LinkedHashMap where String.class was inserted first. The keys of the returned TypeMap are sorted from more abstract to less abstract. If present, Object.class will be the first type in the key set. Autoboxing is enabled in the returned TypeMap.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      Returns:
      a TypeMap that relies on a data structure similar to the Java type hierarchy itself
      See Also:
    • nativeTypeMap

      static <V> TypeMap<V> nativeTypeMap(Map<Class<?>,V> m, boolean autobox)
      Returns a TypeMap that directly implements the Map interface rather than being backed by a regular map. Instead, it relies on a data structure similar to the Java type hierarchy itself. This implementation is sensitive to the insertion order of the types. Thus, if you expect a lot of requests for, say, String.class, it pays to initialize it with a LinkedHashMap where String.class was inserted first. The keys of the returned TypeMap are sorted from more abstract to less abstract. If present, Object.class will be the first type in the key set.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      autobox - whether to "autobox" the types requested via get() and containsKey()
      Returns:
      a TypeMap that relies on a data structure similar to the Java type hierarchy itself
      See Also:
    • nativeTypeMapBuilder

      static <V> TypeMapBuilder<V> nativeTypeMapBuilder()
      Returns a Builder for "native" type maps.
      Type Parameters:
      V - the type of the values in the map
      Returns:
      a builder for "native" type maps
    • greedyTypeMap

      static <V> TypeMap<V> greedyTypeMap(Map<Class<?>,V> m)
      Returns an auto-expanding TypeMap. Autoboxing is enabled in the returned TypeMap.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      Returns:
      an auto-expanding TypeMap
    • greedyTypeMap

      static <V> TypeMap<V> greedyTypeMap(Map<Class<?>,V> m, boolean autobox)
      Returns an auto-expanding TypeMap.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      autobox - whether to "autobox" the types requested via get() and containsKey()
      Returns:
      an auto-expanding TypeMap
    • greedyTypeMapBuilder

      static <V> TypeMapBuilder<V> greedyTypeMapBuilder()
      Returns a Builder for "greedy" type maps.
      Type Parameters:
      V - the type of the values in the map
      Returns:
      a Builder for "greedy" type maps
    • treeTypeMap

      static <V> TypeMap<V> treeTypeMap(Map<Class<?>,V> m)
      Returns a TypeMap that is backed by a TreeMap. Its keys are sorted such that for any two types in the key set, the one that comes first will never be a supertype of the one that comes second - and vice versa: the one that comes second will never be a subtype of the one that comes first. In other words, they are sorted from less abstract to more abstract. If the map contains key Object.class, it will be the last element in the key set. The Comparator used for the TreeMap is similar to the one used for TypeSet.prettySort(), but much more light-weight, and therefore more performant. Autoboxing is enabled in the returned TypeMap.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      Returns:
      a TypeMap that is backed by a TreeMap
      See Also:
    • treeTypeMap

      static <V> TypeMap<V> treeTypeMap(Map<Class<?>,V> m, boolean autobox)
      Returns a TypeMap that is backed by a TreeMap. Its keys are sorted such that for any two types in the key set, the one that comes first will never be a supertype of the one that comes second - and vice versa: the one that comes second will never be a subtype of the one that comes first. In other words, they are sorted from less abstract to more abstract. If the map contains key Object.class, it will be the last element in the key set. The Comparator used for the TreeMap is similar to the one used for TypeSet.prettySort(), but more light-weight, and therefore more performant.
      Type Parameters:
      V - the type of the values in the map
      Parameters:
      m - the map to convert to a TypeMap
      autobox - whether to "autobox" the types requested via get() and containsKey()
      Returns:
      a TypeMap that is backed by a TreeMap
      See Also:
    • treeTypeMapBuilder

      static <V> TypeMapBuilder<V> treeTypeMapBuilder()
      Returns a Builder for "tree" type maps.
      Type Parameters:
      V - the type of the values in the map
      Returns:
      a Builder for "tree" type maps
    • getOrDefault

      default V getOrDefault(Object key, V defaultValue)
      Specified by:
      getOrDefault in interface Map<Class<?>,V>