- Type Parameters:
V
- the type of the values in the map The type of the values in theMap
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
-
Nested Class Summary
-
Field Summary
-
Method Summary
Modifier and TypeMethodDescriptionstatic <V> TypeMap<V>
fixedTypeMap
(Map<Class<?>, V> m) Returns aTypeMap
that is internally backed by a regular, unmodifiableMap
.static <V> TypeMap<V>
fixedTypeMap
(Map<Class<?>, V> m, boolean autobox) Returns aTypeMap
that is internally backed by a regular, unmodifiableMap
.static <V> TypeMapBuilder<V>
Returns aBuilder
for "fixed" type maps.default V
getOrDefault
(Object key, V defaultValue) Throws an UnsupportedOperationException.static <V> TypeMap<V>
greedyTypeMap
(Map<Class<?>, V> m) Returns an auto-expandingTypeMap
.static <V> TypeMap<V>
greedyTypeMap
(Map<Class<?>, V> m, boolean autobox) Returns an auto-expandingTypeMap
.static <V> TypeMapBuilder<V>
Returns aBuilder
for "greedy" type maps.static <V> TypeMap<V>
nativeTypeMap
(Map<Class<?>, V> m) Returns aTypeMap
that directly implements theMap
interface rather than being backed by a regular map.static <V> TypeMap<V>
nativeTypeMap
(Map<Class<?>, V> m, boolean autobox) Returns aTypeMap
that directly implements theMap
interface rather than being backed by a regular map.static <V> TypeMapBuilder<V>
Returns aBuilder
for "native" type maps.static <V> TypeMap<V>
treeTypeMap
(Map<Class<?>, V> m) Returns aTypeMap
that is backed by aTreeMap
.static <V> TypeMap<V>
treeTypeMap
(Map<Class<?>, V> m, boolean autobox) Returns aTypeMap
that is backed by aTreeMap
.static <V> TypeMapBuilder<V>
Returns aBuilder
for "tree" type maps.Methods inherited from interface java.util.Map
clear, compute, computeIfAbsent, computeIfPresent, containsKey, containsValue, entrySet, equals, forEach, get, hashCode, isEmpty, keySet, merge, put, putAll, putIfAbsent, remove, remove, replace, replace, replaceAll, size, values
-
Field Details
-
SOURCE_MAP
- See Also:
-
-
Method Details
-
fixedTypeMap
Returns aTypeMap
that is internally backed by a regular, unmodifiableMap
. Autoboxing is enabled in the returnedTypeMap
.- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
- Returns:
- a
TypeMap
that performs reliably well in many cases
-
fixedTypeMap
Returns aTypeMap
that is internally backed by a regular, unmodifiableMap
.- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
autobox
- whether to "autobox" the types requested viaget()
andcontainsKey()
- Returns:
- a
TypeMap
that performs reliably well in many cases
-
fixedTypeMapBuilder
Returns aBuilder
for "fixed" type maps.- Type Parameters:
V
- the type of the values in the map- Returns:
- a
Builder
for "fixed" type maps
-
nativeTypeMap
Returns aTypeMap
that directly implements theMap
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 aLinkedHashMap
whereString.class
was inserted first. The keys of the returnedTypeMap
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 returnedTypeMap
.- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
- Returns:
- a
TypeMap
that relies on a data structure similar to the Java type hierarchy itself - See Also:
-
nativeTypeMap
Returns aTypeMap
that directly implements theMap
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 aLinkedHashMap
whereString.class
was inserted first. The keys of the returnedTypeMap
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 aTypeMap
autobox
- whether to "autobox" the types requested viaget()
andcontainsKey()
- Returns:
- a
TypeMap
that relies on a data structure similar to the Java type hierarchy itself - See Also:
-
nativeTypeMapBuilder
Returns aBuilder
for "native" type maps.- Type Parameters:
V
- the type of the values in the map- Returns:
- a builder for "native" type maps
-
greedyTypeMap
- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
- Returns:
- an auto-expanding
TypeMap
-
greedyTypeMap
Returns an auto-expandingTypeMap
.- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
autobox
- whether to "autobox" the types requested viaget()
andcontainsKey()
- Returns:
- an auto-expanding
TypeMap
-
greedyTypeMapBuilder
Returns aBuilder
for "greedy" type maps.- Type Parameters:
V
- the type of the values in the map- Returns:
- a
Builder
for "greedy" type maps
-
treeTypeMap
Returns aTypeMap
that is backed by aTreeMap
. 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 keyObject.class
, it will be the last element in the key set. TheComparator
used for theTreeMap
is similar to the one used forTypeSet.prettySort()
, but much more light-weight, and therefore more performant. Autoboxing is enabled in the returnedTypeMap
.- Type Parameters:
V
- the type of the values in the map- Parameters:
m
- the map to convert to aTypeMap
- Returns:
- a
TypeMap
that is backed by aTreeMap
- See Also:
-
treeTypeMap
Returns aTypeMap
that is backed by aTreeMap
. 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 keyObject.class
, it will be the last element in the key set. TheComparator
used for theTreeMap
is similar to the one used forTypeSet.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 aTypeMap
autobox
- whether to "autobox" the types requested viaget()
andcontainsKey()
- Returns:
- a
TypeMap
that is backed by aTreeMap
- See Also:
-
treeTypeMapBuilder
Returns aBuilder
for "tree" type maps.- Type Parameters:
V
- the type of the values in the map- Returns:
- a
Builder
for "tree" type maps
-
getOrDefault
Throws an UnsupportedOperationException.- Specified by:
getOrDefault
in interfaceMap<Class<?>,
V>
-