Closed GoogleCodeExporter closed 9 years ago
What are some specific use cases for this? We already provide
ClassToInstanceMap and friends, which cover some of these areas.
Original comment by wasserman.louis
on 20 Mar 2013 at 5:48
The blogged API is not type safe.
map.get(new TypedMapKey<String>("key")) internally does an unchecked cast, but
the cast isn't safe.
It is no different than:
@SuppressWarnings("unchecked")
<V> V get(Object key) {
return (V) rawGet(key);
}
because in both ways the type of the value isn't guaranteed to be the type that
caller thinks it is.
Original comment by be...@google.com
on 24 Mar 2013 at 12:04
In general, this seems to ask for a heterogeneous map in which having same-type
type tokens doesn't imply that the type tokens are equal.
i.e. For TypeToInstanceMap, there can be no more than one value to a type.
Essentially, give some composite key from:
public static <K, B> Key<K, B> of(K key, Class<? extends B> clas) {...}
Above, key("1", String.class) would not be equal to key("2", String.class); all
the while, the composite key would be a type token to whatever map uses it.
I'd assume this would be analogous to such a map: Map<K, TypeToInstanceMap<B>>;
note that this could have multiple TypeToken<? extends B> to K, whereas care
would be needed if you only wanted one token to a key.
For heterogeneous collections, say, lists, in general:
ArrayList<TypeToInstanceMap<Object>> formalsToActuals; // maps are singletons
...
list.get(0).put(String.class, "str");
list.get(1).get(Double.class)
These approaches are work-arounds, mind you. But useable. Or, did I miss the
point completely?
Original comment by jysjys1...@gmail.com
on 24 Mar 2013 at 11:14
I actually sort of like this, but in a variant form: the key should have no
additional String parameter, and should use reference equality:
static final Key<String> ADDRESS_KEY = new Key<String>();
FWIW, the Java compiler uses essentially this idiom in the Context class
(http://www.docjar.com/docs/api/com/sun/tools/javac/util/Context.html), though
it binds factories instead of objects themselves in what I'd describe as a poor
man's dependency injection.
Original comment by lowas...@google.com
on 3 May 2013 at 9:11
> The blogged API is not type safe.
This is intentional. I needed my map to be a drop in replacement for
java.util.Map.
For Guava, I suggest to implement it differently and maybe support a wrapper
helper that converts between TypedMap and Map.
> the key should have no additional String parameter
I considered this but it makes debugging a nightmare since you can't tell
anymore what each key was (there is no way to return to the Key instance from
within the map).
If Java only had a hook for the part of the compiler that handles "enum" :-(
I also considered using enums for this but the API became too clumsy.
One last comment: Someone suggested to swap the API:
Key<String> ADDRESS_KEY = new Key<String>();
String value = ADDRESS_KEY.getFrom( map );
I don't like it much. It deviates quite a bit from the Map API that we're used
to. But it might be a case of "reject great new idea because of tradition".
Original comment by adigu...@gmail.com
on 5 May 2013 at 2:42
Did not know that this had replies...
I'll add that I've worked with, what I'd call, a more general solution that,
while a bit involved, does well enough with this problem.
Consider a structure:
class Actual<T> {
final T value;
final TypeToken<T> formalType;
TypeToken<T> getActualType() {
// memoize, since Actual is likely to be an immutable class
return value==null? formalType: formalType.getSubtype(value.getClass());
}
}
The above effectively types a value and may even preserve the generic type of
an actual type given a generic formal type:
List<String> list = new LinkedList<String>();
Actual.of(list, Object.class).getActualType(); // LinkedList
Actual.of(list, new TypeToken<List<String>>(){}).getActualType(); // LinkedList<String>
Use actuals as values to maps (Map<K, Actual<? extends V>>), and, if there is
ever an interface or subclass of Actual, perhaps go the route of the Multimaps
and provide a Supplier:
interface ActualMap<K, V> extends Map<K, V> {
...
Map<K, Actual<? extends V>> asMapOfActuals();
// Likely fully-operational if Actual is a final class
}
An implementation to ActualMap<K, V> may find it advantageous to explicitly
aggregate the V parameter type as a TypeToken; put(K, V) for maps with a
generic value type parameter could possibly preserve generic-type values;
otherwise, maps whose values are known to be generic would not be able to store
put(K, V) values as generic values:
V put(K key, T value, TypeToken<T> typeToken) ...
V put(K key, V value) {
return put(key, value, preservedType.getSubType(value.getClass()));
// may deal with warnings
}
Lastly, concerning the ActualMap API, I had personally opted for
Predicate/Function usage for contains/(get|remove) respectively:
contains(Object, TypeToken<?>, BiPredicate<? super TypeToken<?>, ? super
Actual<? extends V>>)
contains(Object, Predicate<? super Actual<?>>)
[get|remove](Object, TypeToken<T>, BiFunction<? super TypeToken<T>, ? super
Actual<? extends V>, ? extends T>)
[get|remove](Object, Function<? super Actual<? extends V>, ? extends T>)
I defined common predicates and functions in a Actuals utility class:
equalsFormalType() : BiPredicate<TypeToken<?>, Actual<?>>
isAssignableFromFormalType() : BiPredicate<TypeToken<?>, Actual<?>>
equalsActualType() : BiPredicate<TypeToken<?>, Actual<?>>
isAssignableFromActualType() : BiPredicate<TypeToken<?>, Actual<?>>
equalsFormalType(TypeToken<?>) : Predicate<Actual<?>>
isAssignableFromFormalType(TypeToken<?>) : Predicate<Actual<?>>
equalsActualType(TypeToken<?>) : Predicate<Actual<?>>
isAssignableFromActualType(TypeToken<?>) : Predicate<Actual<?>>
castActualValue() : BiFunction<TypeToken<T>, Actual<? extends U>, T>
castActualValue(TypeToken<T>) : Function<Actual<? extends U>, T>
Finally, to support usage, I defined a key/type-token entry type, Option<K, V>
when defining static final keys:
public static final Option<String, Integer> ID = Option.of("id", Integer.class); //
public static final String MEASURES = "measures"; // could be one of [Iterable<Double>, double[]]
An operation in a utility class could be used to adapt maps to actual maps:
static ActualMap<K,V> of(Map<K, V> map) {
if (map instanceof ActualMap<?, ?>)
return (Actual<K, V>) map;
final Actual<K, V> m = new LossyActualMap<>();
m.putAll(map);
return m;
}
This was a bit involved in implementing, frankly, but I feel as if it works as
intended, and gives a be of flexibility as to how the types of values are
viewed before retrieving or removing them (TypeToInstanceMaps can't given that).
Original comment by jysjys1...@gmail.com
on 22 Aug 2013 at 12:26
Of course, the aforementioned Context works as well if willing to sacrifice
flexibility in key parameter type.
Original comment by jysjys1...@gmail.com
on 22 Aug 2013 at 12:31
Actual/ActualMap looks interesting but IMO solves a different problem: Type
preservation. My approach allows you to put Actuals into a typed map, so it's
more flexible since it doesn't impose an artificial limit for the value type.
Places where I used this:
- Options / preferences to distinguish between int, boolean and String values.
- data elements in models which allow to attach arbitrary data to an instance
(SWT, Swing and ZK use this but without type safety).
The biggest advantage of the constant keys is that it's easy to find all places
where a value is being used (as opposed to String constants).
Original comment by adigu...@gmail.com
on 7 Sep 2013 at 6:27
This issue has been migrated to GitHub.
It can be found at https://github.com/google/guava/issues/<issue id>
Original comment by cgdecker@google.com
on 1 Nov 2014 at 4:12
Original comment by cgdecker@google.com
on 1 Nov 2014 at 4:18
Original comment by cgdecker@google.com
on 3 Nov 2014 at 9:08
Original issue reported on code.google.com by
adigu...@gmail.com
on 20 Mar 2013 at 5:45