Reputation: 4563
I have about a month of work into a system that is using a guava datatype of
ImmutableMap<String, Object>
Things work great, even my developers love it. Its awesome. Until yesterday. We have a recordset that we have to bring in as an ImmutableListMultimap and we have to cast it to our ImmutableMap type.
here is our compile error
ImmutableListMultimap<String, Object> tst = new bla bla bla;
ImmutableMap<String, Object> tst2 = (ImmutableMap<String, Object>) tst.asMap();
The error itself is
Cannot cast from ImmutableMap<String,Collection<Object>> to ImmutableMap<String,Object>
Now doing this cast isn't an option. It must be done or we have to go back and reengineer a month of work. Not going to happen. The only option I have right now to solve this is to build a factory class that iterates the tst
variable, recasting the values to basic objects and dumping that into tst2
. But, this tst var could contain thousands of entries which makes that solution unwise.
Another option is Apache Collections Transforms, but since those are just iterators as well, I don't gain much.
Is there anything more elegant that I can do to downcast a generic or has Sun's brilliant concept of generics type erasure just doomed me.
Upvotes: 0
Views: 219
Reputation: 198321
So, one approach that will not generate any compile warnings would be, instead of a cast, to use ImmutableMap.copyOf
. This will not actually perform a copy, as it can detect that would be redundant, but it will take an ImmutableMap<? extends K, ? extends V>
and return an ImmutableMap<K, V>
.
It will still be a constant time operation, and a cast under the surface, but it will not generate warnings.
Upvotes: 0
Reputation: 140494
You can use the ImmutableMap.copyOf
method, with explicit constraints:
ImmutableMap<String, Object> tst2 = ImmutableMap.<String, Object>copyOf(tst.asMap());
Note that this doesn't necessarily copy the map - under some (many) circumstances, tst == tst2
(they are identical references).
If you absolutely must avoid making a copy of the map, you would need to use a wildcard type, since ImmutableMap<K, Object>
and ImmutableMap<K, Collection<Object>>
are not related types:
ImmutableMap<String, ?> tst2 = tst.asMap();
The fact that the value is a wildcard type shouldn't affect your ability to do anything with the map values as Object
s:
for (Map.Entry<String, ?> entry : tst2.entrySet()) {
Object value = entry.getValue();
//...
}
Upvotes: 3
Reputation: 46482
It can't be cast for a good reason, but there's simple workaround:
ImmutableMap<String, Object> tst2 =
(ImmutableMap<String, Object>) (Object) tst.asMap();
This looks possibly a bit saner:
ImmutableListMultimap<String, Object> tst = ...
ImmutableMap<String, ? extends Collection<Object>> asMap = tst.asMap();
@SuppressWarnings("unchecked")
ImmutableMap<String, Object> tst2 = (ImmutableMap<String, Object>) asMap;
The generic system is not perfect (if it was, it'd even more complicated) and sometimes it's hard to understand. The unchecked cast above is safe because of the immutability: There's no way to put in an Object
(this would go against the Map<String, Collection<Object>>
signature).
Upvotes: 2