Map<String,String> Integer cannot be cast to class String - java

There's a way to handle this validation ?
class java.lang.Integer cannot be cast to class java.lang.String
Code:
private List<Map<String,String>> getEmpRegistry(
List<Map<String,String>> tblObjRowsList,
Integer rowNumber
){
List<Map<String,String>> empRegistry = new ArrayList<>();
for (Map<String,String> empR : tblObjRowsList){
if (empR.get("id").equals(rowNumber)){
empRegistry.add(empR);
}
}
return empRegistry;
}
Looks wrong, because I can't compare String and Integer.
But for some reason tblObjRowsList is extracted from stored procedure in MySQL via Spring Data:
#Query(value="CALL SP_ObjsRowDetails2(:Id,:templateId,:strMode)",nativeQuery = true)
List<Map<String,String>> getRowDetail (#PathParam("Id") Integer id,
#PathParam("templateId") Integer templateId,
#PathParam("strMode") Integer strMode);
Extract the data like this:
I tried:
Integer.parseInt(empR.get("id")).equals(rowNumber)
Integer.parseInt(empR.get("id").toString()).equals(rowNumber)
empR.get("id").toString().equals(String.valueOf(rowNumber))
And the most interesting thing is:
Doesn't make sense, if the source variable is Map<String,String>

That declaration of List<Map<String,String>> getRowDetail(...) is probably a "lie". That's the problem with runtime, dynamic code generation...
Spring data generates the implementation for you, and there is no proof that it will generate a Map<String, String> in reality, it all depends on what the SQL type will actually be in your procedure
Spring can and will produce a Map but you can not know what types lie therein, the actual types will be chosen once the metadata for the SQL result set comes back.
The compiler is happy, because generics are erased after compilation anyway, so as long as it's a Map, it's fine.
In the end you only realise Spring did not do "the right thing" (or actually, what you expected, because in its view it did the right thing) until runtime.
There is no easy way out. Either you fully acknowledge the unknowable nature of the contents of the map, and declare a Map<Object, Object> as the return type. And your code will have to cast to operate on the data.
Or you make sure you adapt the Java to the SQL or vice-versa with less generic types (eg <String, Integer>) - and find a way to make sure nobody changes the SQL without applying the change to the Java side, or vice-versa.

I believe that instead of Map<String, String> use Map<String, Object> and then according to the type inside of the value just cast it to to integer, string, or whatever.

Related

Java Generics - Mixed Object Maps

I'm still getting used to Java Generics however I'm currently in the process of updating an application written prior to generics to use the latest version of java.
Problem is the code was not written with type safety in mind!
We have a whole bunch of Maps that basically hold various object types including strings. For example:
Map map = new HashMap();
map.put("key1", "String1");
map.put("key2", new Date());
map.put("key3", new CutsomClass());
Now I'm still struggling with the best way to handle these without getting into refactoring a whole lot of code. Refactoring is not an option at this time.
Currently I can't see anything past Map<String, Object> although Map<String, ? super Object> works but I think its essentially the same thing ?
I'm still struggling with the best way to handle these without getting
into refactoring a whole lot of code
So don't change them at all. The raw types - that is, the non-generic types - are still technically valid. It's not ideal and it will generate a compiler warning but the code will work (well, work as well as it ever did).
All classes extend Object so you can put any value you want into the following map:
Map<String, Object> map = new HashMap<>();
You get an additional guarantee that the key is a string, so its somewhat better than using the raw type.
Basically though, you should really try to avoid using a map if you can't define the type of the key or the value.
As of now, you can only replace the raw type Map with Map<String, Object>; but that type information is close to "useless". Unless you refactor your whole component to deal with different map objects, there isn't much you can do. Of course, you can get rid of the type warnings, but you still have to do instanceof checks each time you access a Map value.
On way out of this: assuming that number of "value" types is known and reasonably small, you could create a bunch of helper methods that go like:
public Map<String, Date> extractDates(Map<String, Object> allValues) {
...
This method could implement a "wrapper" around the allValues map that only provides those map entries that are actually Date objects.
Meaning: you keep your current map object, but you provide "more specialized" views on that map. That allows you to write new code exploiting the additional type information.
But of course, this doesn't come for free. It adds certain complexity, and defining the exact details of such "view maps" might turn out to be rather complicated.
There is little you can do to achieve the full static type checking available with Generics used properly. However, I don't believe you must abandon type checking completely and rely on run-time casting in this case. I think you can go one step towards that.
I assume this is a common map that the code uses for general storage, perhaps for persistence or properties etc. If this is the case then you can at least do something like this:
class AnyMap<K> {
final Map<K,Object> map;
public AnyMap(Map<K, Object> map) {
this.map = map;
}
public <V> Map<K,V> as(Class<V> theClass) {
return (Map<K,V>) map;
}
}
public void test() {
AnyMap<String> commonMap = new AnyMap<>(Collections.EMPTY_MAP);
// Use this one as a Date map.
Map<String,Date> dateMap = commonMap.as(Date.class);
// This one as a String map.
Map<String,String> stringMap = commonMap.as(String.class);
}
This is a kind of Map holder that can then deliver the map as a proper generic object with the correct bounds. Hopefully you will find that certain modules will use the common map exclusively for Dates and others will use Strings. In areas such as these you can use Map<X,Y> as(...) to give you a properly statically-checked map for that module/section and use that exclusively in that code section.
As Michael suggested Map<String, Object> map = new HashMap<>();
is a first step.
However, it assumes that you have only String as keys and you will probably need to cast objects you get from the map.
I think that a second step would be to split this map into multiple maps declaring a more specific type :
Map<String, Date> mapDates = new HashMap<>();
Map<String, String> mapStrings = new HashMap<>();
Map<String, CustomClass> mapCutsomClasses = new HashMap<>();
Dont use raw types... see this to know why..
Now, you can break it out as following, so you can get it:
your map<K,V> has as keys Strings only, so K = string will be correct, on the other hand "String1", new Date() and new Custom Class seems to have nothing in common, but wait, all the classes in java are actually inheriting the Object class... that means you can do V=Object
now your map can be declared as Map<String, Object> and all this
map.put("key1", "String1");
map.put("key2", new Date());
map.put("key3", new CutsomClass());
will be ok
Conversion from non-generics (e.g. old Java) to generics can be a real PITA. The easiest way to do it is to replace each Map, Set, List with related generic, e.g.
Map<String, Object> map = new HashMap<>();
but only if mapped objects are not generics too (or are generics just used for reading). E.g. if in your code had something like
Map hasmap = new HashMap();
hashmap.put("blah",123)
map.put("keyX",hashmap);
In such cases, when you find map getter, and you will have in original code a put, you will have lot of troubles:
Map hashmap = (HashMap)(map.get("keyX"));
Integer value = hashmap.get("blah");
hashmap.put("otherkey","mooo");
In this case, you can't have clear code: if you use question marks, you will face errors, like in
Map<?,?> hashmap = (HashMap<?,?>)(map.get("keyX"));
Integer value = (Integer)hashmap.get("blah"); // this works
hashmap.put("otherkey","mooo"); // this crashes
so you have two alternatives: rewrite code (to avoid warnings), or force everything at Objects, and receive Unchecked Casts warnings.
#SuppressWarnings("unchecked")
Map<Object,Object> hashmap = (HashMap<Object,Object>)(map.get("keyX"));
Integer value = (Integer)hashmap.get("blah"); // this works
hashmap.put("otherkey","mooo"); // this works too
Further details about question marks and generics can be found here: What is the difference between ? and Object in Java generics?
Looks like you want to quickly port the old code in and also you want to move the old code towards strict type safety without refactoring a large code base. Keep your old code by porting it in using this:
Map<String, Object> oldMapPorted = new HashMap<>();
New code written in this app can use a technique like this for strict type safety:
Map<String, Date> newMapDates = new HashMap<>();
Map<String, String> newMapStrings = new HashMap<>();
Map<String, CustomClass> newCutsomClasses = new HashMap<>();
A new class can be created for future edits and enhancements while the old code still has the same potential instability as usual.

HashMap containing enums

Sorry, new to java, actally coding in jython but I would think java syntax should work.
I have a hashmap that looks like this:
Hashmap = {21035179={WEIGHT=1}, 2300={WEIGHT=0}, 21035180={EMA_FIRST=1000.11615393169158, EMA_SECOND=966.8684105279658}}
The values are of an enum type, not sure how that changes things cause I'm not that familiar with that type.
I want to get the weight of 2300, I would think the syntax for this is:
Count = 2300
Hashmap.get(Count).get(enum.WEIGHT)
but this doesn't work, I get a None type back.
What am I doing wrong here?
Answering for java, your structure seems to be Integer -> enum -> double. This would be represented in Java as:
public enum Field {
WEIGHT, EMA_FIRST, EMA_SECOND;
}
Map<Integer, Map<Field, Double>> myMap
Because you are using an enum as the key, you should create the values as EnumMap:
myMap.put(2300, new EnumMap(Field.class));
myMap.get(2300).put(Field.WEIGHT, 34.7);
Getting the weight for 2300 would then be myMap.get(2300).get(Field.WEIGHT).

Convert Object to Array [Java]?

I have an Object (Object o) which when I call o.toString() it prints:
{data-ved=0EMIuCBcoAA, data-pid=23, href=https://mail.google.com/mail/?tab=wm, class=gb_P}
As you can see there seems to be an array of Strings in this object. Is there a way to directly convert the object to a String[]?
The expected result would be a String[] containing something like:
data-ved=0EMIuCBcoAA
data-pid=23
href=https://mail.google.com/mail/?tab=wm
class=gb_P
Currently, I'm using Regex to extract the parts of the String I need (as array).
Is there a better way to do this?
If it matters, the object is being returned from a JavaScript Executor in Selenium.
As can be seen in the comment trail the object that is returned seems to be a Map (and probably a Map<String, Object> or Map<String, String>). Thus you could do the following:
if( o instanceof Map ) {
Map map = (Map)o; //If you know the generic type you could use Map<String, Object> etc. instead
String dataVed = (String)map.get("data-ved");
...
}
As you can see casting the value is needed as well since there's no generic type provided for the map and thus all you know is that keys and values are objects of any class.
If you know what generic type should be used you could improve that, especially if the value type is String. Unfortunateley due to type erasure you can't get that type at runtime so the only way to get to know what to expect would be some documentation.

How to cast a Session object to Map?

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.

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

Categories