How to cast a Session object to Map? - java

I am converting old java code to support generics and came across this line of code which was trying to cast an object retrieved from the session into a TreeMap:
TreeMap allTransactions = (TreeMap) pageContext.getSession()
.getAttribute("allTransactions");
When I tried to convert it to specific type:
TreeMap<String, MyDataBean> allTransactions = (TreeMap<String, MyDataBean>)
pageContext.getSession().getAttribute("allTransactions");
It gave me a warning:
Type safety: Unchecked cast from Object to TreeMap<String,MyDataBean>
In an effort to get rid of the warning completely, I wrote a method to cast it to Map:
public static <K,V> Map<K,V> castToMap(Class<? extends K> clazz1,
Class<? extends V> clazz2, Map<?,?> c) {
Map<K,V> map = new TreeMap<K,V>();
for (Map.Entry<?,?> entry : c.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
map.put(clazz1.cast(key), clazz2.cast(value));
}
return map;
}
Goodnews: This time I did not get any error when I modified the initial code to call this method:
Map<String, MyDataBean> allTransactions = MyUtilityClass.castToMap(String.class,
MyDataBean.class,
(Map<?,?>)pageContext.getSession().getAttribute("allTransactions"));
But I still had to cast it ^^^here to call my function.
Question 1:
Why it does not show any errors now when I am still using the cast (Map<?,?>) versus what I tried before (TreeMap<String,MyDataBean>)?
Question 2:
My app is broken as of now since other developers have not committed the code yet, and so I am in no position to run the App and verify its correctness. Can anyone tell just by looking at it, if this casting will behave the same way as before?

If it is a Map then it is implicitly a Map<?,?> - as in, a map of anything. It considers that to be a safe cast because, since it is a map, it cannot fail. At this point, it's no different than casting it to any other object, which they're assuming is safe.
If it is a bad cast (say it's a List, not a Map) then it will explode there before anything else goes wrong. But because those types are lost at compile time, it will happily continue if the problem is in the generic types. So Map<?,?> is considered safe because it will explode because of being not being a Map if that's the problem.
As far as your second issue, it looks fine, but without more context it's hard to say. That being said, you should be able to use the history in your source control to grab an earlier version to compare against. If you can't obtain earlier versions of your code, your source control isn't very useful, now is it?

Ad 1.: The cast itself is safe, because you don't specify any types which the compiler can not match against the method signature. Java 4 List can be safely assigned to Java 5 List<?>, but when you specify a type, you assume something, the compile can not safely check (therefore the warning).
Ad 2.: You have substituted a "hard" language cast with a "soft" method call cast, which will fail equally, if the classes are not castable into one another - I prefer the less verbose version of language casting, because one should rely one one's API.

Related

How to add values for java generic map with undetermined "?" value type

I've seen this kind of declaration in jdk 8 sample:
Map<String, ?> map = new HashMap<>(3);//OK
But when I tried to add value to "map", I didn't succeed:
map.put("abc", Optional.of(5));
map.put("kk", "xyz");
Both fail to compile. I wish to know:
(1) What does "?" indicate in Map declaration above?
(2) How to give values to this "map"?
Map<String,?> is an abstract type. If a variable has this type, it could reference an object with any of the following types (and others).
HashMap<String,Integer>
TreeMap<String,String>
HashMap<String,ArrayList<Boolean>>
TreeMap<String,Throwable>
Obviously, the possibilities are basically endless. If you have a variable of this type, you know that it refers to a map whose keys are String, but you really know nothing else. In particular, you don't know what type of object you'll end up with when you do a get on that Map.
More importantly though, you will never be able to put anything into the map, without some kind of nasty, unsafe casting operation. The compiler will stop you. So in the examples you've given -
map.put("abc", Optional.of(5)); won't compile, because map could be a HashMap<String,String>, into which you can't put an Optional.
map.put("kk", "xyz"); won't compile, because map could be a TreeMap<String,Integer>, into which you can't put a String.
The exceptions would be null, or any value that has come from the map itself - see Andy Turner's excellent answer for more detail about those possibilities.
In short, if you have a variable of type Map<String,?>, the operations that the compiler will let you do to it are a bit limited. You can't put anything into the map, unless it's null or it's already in the map. All you can do is get values from the map, and remove values from the map.
So using a Map<String,?> variable is very limiting. If all you want to do with the map is read values from it, this is just fine, of course. But don't expect to be able to insert arbitrary values into the map, unless you use a different expression to refer to the map.
Question (1)
? is called wildcard generic type.It can be used with any type,
but you need to specify type fist as Map is an abstract class.
Question (2)
for you to give value to this map its either you use Upper bound or Lower bound.
The following are guidelines when using upper bound or lower bound
? extends Type - is used for read access only
? super Type - is used for write access only.
PECS in short Produces(write acccess) -uses Extends while Consumes(read access) - uses Super
Map<String, ? super Object> map = new HashMap<>(3);//OK
map.put("abc",Optional.of(5));
map.put("kk","xyz");
map.forEach((k,v)->System.out.println(k+"\t"+v));
Output
kk xyz
abc Optional[5]
Process finished with exit code 0
Additional Notes
Unbounded wildcard ?
List<?> l = new ArrayList<String>();
Wildcard with an upper bound ? extends type
List<? extends Exception> l = new ArrayList<RuntimeException>();
Wildcard with a lower bound ? super type
List<? super Exception> l = new ArrayList<Object>();
? is an unknown type: you don't know exactly what it is at that point in the code.
Because of that, you cannot safely add any value to the map, with a couple of exceptions.
You can add literal null (because null can be cast to any type):
map.put("abc", null); // compiles
Also, you can add a value to the map which you obtain from the map:
<T> void reAdd(Map<String, T> map) {
T value = map.get("123");
map.put("456", value);
}
// Call with
reAdd(map); // compiles

Java/Eclipse: returning to List casted Map.values()

I'm working on a Java based web application and found suddenly a behavior which I can't understand at the moment.
Let's say I'm having the following code:
...snip...
Map<Integer, Foo> map = new HashMap<Integer, Foo>();
...snip...
public List<Foo> getFoos() {
return (List<Foo>)map.values();
}
I know that you need to instantiate a new List like new ArrayList() for example but I wonder why eclipse is not giving me a warning.
Only when running the code I (would) get a ClassCastException. I'm quite sure that this is not a bug of eclipse as the compiler also has no problem with this code but could anyone explain me why you get always an error when running this code but IDE and compiler don't complain?
Both the IDE and the compiler only inspect on a syntactical level. And on the syntactical level, everything is fine. The type of the result of getFoos() is of the valid type List due to the cast, and everything else is alright as well. This is actually a mistake in the semantics. On the semantic level, you are trying to cast the result of map.values(), which will actually return an instance of an internal type of HashMap that derives directly from AbstractCollection, to List. Since this is no valid cast (List is no supertype of the result of map.values()), java will throw a ClassCastException on runtime.
According to the javadoc, Map.values() returns a Collection<V>, which means that there is no guarantee that it will be List. This depends on the internal implementation of the specific map (in your case, the implementation of the HashMap#values() method returns an implementation of the AbstractCollection interface, called Values and this is not a List).
Better change your method's return type to Collection<Foo> and remove the cast.
Map.values() is supposed return a Collection. Since all Lists are Collections, an implementation of Map.values could return a List, and the cast would be correct. At compile time there is no way to prove that the map's values Collection will be a List, but also no way to disprove it, so the compiler allows the code, but inserts a runtime check.

Have SortedMap<String, String>, need SortedMap<String,Serializable>. What to do?

This question regards gernerics, type-safty and Collection classes in Java:
Just as the title says: I have a method that returns a Collection, SortedMap<String,String>, that I retrieve from say methodStrStr() as return value. Say I call this method from methodStrSer() that just needs to pass the retrieved map along but has to return SortedMap<String,Serializable>.
What is an elegant non computationally expensive way to do so?
Without the generics I would have just returned the original map as String is Serializable. However I see that I might run into trouble at runtime if the underlying implementation of the SortedMap would be specific to String objects and result in errors if I wanted to add a different typed object such as Boolean which is also Serializable but not a String. So I am aware that it makes sense, that the compiler does not allow to return a SortedMap<String,String> as a SortedMap<String,Serializable>.
However, I the question remains. Is there even an acceptable way to somhow "transform" (ie a way that does not take O(N) time) the original map type to the target type?
There are several options:
Copying the map - not recommended, because it may be expensive and not necessary
As izstas suggested: Changing the signature of the receiving to accept SortedMap<String, ? extends Serializable> (also see What is PECS (Producer Extends Consumer Super)? )
Just provide an appropriate view on the map.
The latter may be the most appropriate here. You can simply write
SortedMap<String, String> oldMap = ...;
SortedMap<String, Serializable> newMap =
Collections.<String, Serializable>unmodifiableSortedMap(oldMap);
This is safe, because the map is unmodifiable, and thus can not be "polluted" with Serializable objects that are not String. (A simple cast, resulting in a modifiable map, would of course not be type-safe - see this answer for an example why).

Should return statements use generics in Java?

I have a basic question about using the "Best Practices" in coding. (I'm using Java, but the question is general to OOP.) When writing method's for a class that are intended to be used in the long run, is it best to leave the return object with or without generics?
To be specific in my case, I'm returning a Map<String, Integer> with the method. Should I specify this in the return statement, or should I simply return a Map?
It is best to use generics whenever possible. It will help avoid runtime exceptions, and it won't force the people using your code to do a bunch of ugly type casting. For example, if you use the following signature:
Map<String, Integer> getMap();
... then the consuming code might look like this:
Map<String, Integer> map = getMap();
Integer val = map.get(key);
... but if you use a signature like this:
Map getMap();
... the consuming code might look like this:
Map<String, Integer> map = (Map<String, Integer)getMap();
Integer val = map.get(key);
By using generics, not only do you save that (Map<String, Integer>) cast, but in the event that you change getMap to actually return a Map<String, Object>, you will get a compile-time error (which is easy to catch and fix), rather than possibly getting an exception when you call map.get(key) and the JRE tries to do an automatic cast of some random Object into an Integer.
You should definitely return a Map<String, Integer> instead of a plain Map if it makes sense in your method, as this will make it easier for others to use said method - after getting the Map<String, Integer> they will be able to retrieve String keys and Integer values without having to cast them from a generic Object every time (this also makes it a little more typesafe as this way they will know what the keys and values are without even reading the javadoc for your method).
So in short, definitely, return generics.
If you are returning a collection, you should include the generic type that is contained by the collection. For example:
public Map<String, Blammo> getBlammoMap(...)
is (IMHO) preferred to
public Map getBlammoMap(...)
Because it
Limits the options of a bad cast (i.e. kapowMap = (Map<String, Kapow> getBlammoMap()).
Tells the consumer what the method is actually returning.
If the method is clearly intended to work with a certain type (i.e. only String), then it's ok to return a List<String>. If the method is generic taking a type parameter T, you can return List<T>.
I would not simply return a Map, because usually it causes confusion and more boiler-plate code to convert to the desired type.
In general, your type parameters, both input and output, should capture the level of specificity of the precise function. In functional programming, they go so far as to say "the types are the documentation." If I were to see Map foo(Arg args) I would think that foo is in no way concerned with the types in the Map it returns, but somehow relies on Args for something." If I were to see Map<T,String> foo(T t, Arg arg) or Map<T, U> foo(T t, U u) I would think "OK, foo produces a Map based on the type of its t and with a String produced by Arg (or by the U from u)."
In terms of preference, clearly you want to be as clear as possible to the future programmer (yourself or others). Just as returning Map without type-params is obscure, so too would returning Map<String, Integer> might be overly restrictive (and thus misleading) if your foo would work equally well with, say, Map<String, Long>
I believe that more specific, the better. If your method is return a map that always has Strings as the key, and Integers as the value, then definitely use the Map has the return type. That way, any calling code knows exactly what they're getting. If the return type was just Map, then the calling code would have no idea what the class the keys and values are (Other than Object).
In general, you should probably always specify paramerize Maps, Lists, etc., so it's known exactly what it contains. This is very helpful when iterating over them and you can use a java foreach.
for (String currKey : myMap.keySet())
{
System.out.println("curr Key: " + currKey + " curr Value: " + myMap.get(currKey));
}
This eliminates any extra iterators or casting.
Ho-ho-ho! A pretty New Year question.
You generally must (MUST) return a proper generic Map<Whatever, YouNeed>. It may sound crazy, but as soon as you use any generic type without type parameters, you're getting into trouble.
The trouble will be as follows: raw types, being used in the code, change the way methods (even seemingly non-related ones) are resolved. Find a presentation by Joshua Bloch and Bill Pugh called "Java Puzzlers: Scraping the Bottom of the Barrel" for details whle I'm preparing an example :) The video with details is at http://www.parleys.com/#id=2168&st=5 (you may want to scroll to slide 44, 5. "Glommer Pile")
So here's an example:
/**
* (c) (as far as I know) Joshua Bloch and Bill Pugh, 2010
*/
public class Glommer<T> {
String glom(Collection<?> objs) {
String result = "";
for (Object o : objs) result += o;
return result;
}
int glom(List<Integer> ints) {
int result = 0;
for (int i : ints) result += i;
return result;
}
public static void main(String args[]) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Glommer().glom(strings));
}
}
Question is, whether it
prints 6
prints 123
throws an exception,
or does something else.
Try to guess. Then compile (yes it compiles) and see what happens.
Now that does not apply to your case. But having a habit of always specifying the type, even if it will be just Map<?,?>, is extremely helpful. You won't lose.
The obligatory Java Generics FAQ link

How to fix unchecked call warning in Java?

Here is the code I have written?
Set keys = map.keySet();
SortedSet s = new TreeSet(keys);
The warning I'm getting is:
warning: [unchecked] unchecked call to TreeSet(java.util.Collection<? extends E>) as a
member of the raw type java.util.TreeSet
How do I get rid of the compiler warning?
Ideally, start using generics fully. You haven't shown what the type of map is, but ideally you should be able to write something like:
Set<String> keys = map.keySet();
SortedSet<String> s = new TreeSet<String>(keys);
That would be in the case where map was something like a Map<String, Integer>.
If map itself is a raw type, it's harder - again, the best fix would be to start adding generics throughout your code base, getting rid of raw types. That's not always possible if the map is returned from third party code, of course. In that case, you may need to suppress warnings on one line as you convert from raw types to generic types - possibly via Collections.checkedCollection - but after that, you should be able to work with the generic type "properly". For example:
#SuppressWarnings("unchecked") // Just for this one statement
Collection<String> keys = Collections.checkedCollection(map.keySet(),
String.class);
// Now this statement is fully generic with no warnings
SortedSet<String> s = new TreeSet<String>(keys);
As far as this problem is concerned, you should use parameterized type of keys e.g
Set<TypeOfKeyObject> keys = map.keySet();
SortedSet<TypeOfKeyObject> s = new TreeSet<TypeOfKeyObject>(keys);
where TypeOfKeyObject is object type of Key in your map object.
you may force supress the warnings (as already correctly suggested) but not advisable.
At the risk of sounding condescending, I would suggest you to study generics. A good starting point would be this: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

Categories