I am exploring Java and while trying type-casting I ran into the following issue, the casting failed when I called the method the first time but after constructing the List it worked fine.
Looking for a clear explanation to this please :) Thanks
import java.util.*;
import java.util.Set;
import java.util.HashMap; // import the HashMap class
public class Main{
public static void main(String[] args)
{
Set<Map<String, ?>> rows = new HashSet();
HashMap<String, String> map = new HashMap<String, String>();
map.put("1","one");
map.put("2","two");
HashMap<String, String> mapp = new HashMap<String, String>();
mapp.put("3","three");
mapp.put("4","four");
rows.add(map);
rows.add(mapp);
//WHY THE FOLLOWING DOESN'T WORK ?????
//printItems((List<Map<String, ?>>) rows);
//BUT THE FOLLOWING WORKS FINE
List<Map<String, ?>> listedRows = new ArrayList<>(rows);
printItems(listedRows);
}
public static void printItems(List<Map<String, ?>> items) {
for (Map<String, ?> str: items)
System.out.println(str);
}
}
CodePudding user response:
It doesn't work because the run-time type of rows
is HashSet
and HashSet
does not implement the List
interface.
It works when you create listedRows
because the run-time type of that object is ArrayList
which does implement the List
interface.
In your example, you could just just use the Collection
abstraction in printItems
since all you need to do is iterate through items. This will allow you to invoke the method with a Set
or List
(or any other Collection
) without having to cast or recreate an object.
public static void printItems(Collection<Map<String, ?>> items) {
for (Map<String, ?> str: items)
System.out.println(str);
}
On casting if you are interested:
Casting generally only works when you have an object with a compile-time type that is a parent class of the run-time type and you need to coerce the compile-time type of the object to be the run-time type so that you can invoke a method that is specific to the run-time type.
For example (albeit contrived):
public void addTenObjects(List l) {
if (l instanceof ArrayList)
((ArrayList)l).ensureCapacity(10)
for (int i = 0; i < 10; i )
l.add(new Object())
}
CodePudding user response:
Both Set
and List
interfaces extend Collection
, but they don't extend each other. I.e. they are sibling, therefore you can't convert one into another via casting.
Since your method printItems
simply printing the contents of the argument passed into it, you can use instead of it Java 8 Iterable.forEach()
, which expects a Consumer
representing the action:
rows.forEach(System.out::println);
No need to type-cast or copy anything, and no need in introducing the method when you have a fairly simple logic like in this case.