Consider the following code:
public class MainClass {
public static void main(String[] args) {
ArrayList<HashMap<String, Integer>> collection = new ArrayList<>();
ArrayList<HashMap<String, Integer>> outCollecttion = <String, Integer, HashMap<String, Integer>, ArrayList<HashMap<String, Integer>>>doSomeWork(collection);
}
public static <V extends Object, U extends Object, K extends Map<V, U>, J extends Collection<K>> Collection<K> doSomeWork(J collection) {
Collection<K> result = new ArrayList<>();
for (K element : collection) {
result.add(element); //here is supposed other code, this is just example
}
return result;
}
}
I want to do some work on a generic collection that contains map of some generic types. I know that Java has hard time figuring complex generic expressions, so I put explicit types before method call:
<String, Integer, HashMap<String, Integer>, ArrayList<HashMap<Integer, String>>>doSomeWork(collection)
But compiler will not compile that. I do understand that it might have something to do with the fact that I'm trying to use generic type in generic type, but I don't know how to write this method without using casts and than deprecating warnings (I usually compile with -Xlint:unchecked flag).
First, when you want to explicitly provide the type arguments, you have to call the method from its class:
... = MainClass.<String, Integer, HashMap<String, Integer>, ArrayList<HashMap<String, Integer>>>doSomeWork(collection);
Second, your method returns Collection<K>, but the variable is declared ArrayList<...>. The following works for me:
Collection<HashMap<String, Integer>> outCollecttion = ...
You don't need to use generics in assignment.
Collection<HashMap<String, Integer>> outCollecttion = doSomeWork(collection);
Related
This question already has answers here:
Difference between Class and Class<?>
(4 answers)
Closed 9 years ago.
I'm curious is there any real difference between usage of raw Class type and generic Class<?> one? I actively use both Java SE 6 and SE 7.
At least Oracle generics tutorial has no answer to this question and my initial try to google around did not bring any suitable result.
Thank you in advance.
Class is actually not raw type, it is called unknown type or unbounded wildcard
From Java doc:
Unbounded Wildcards
The unbounded wildcard type is specified using the wildcard character
(?), for example, List<?>. This is called a list of unknown type.
There are two scenarios where an unbounded wildcard is a useful
approach:
If you are writing a method that can be implemented using
functionality provided in the Object class. When the code is using
methods in the generic class that don't depend on the type parameter.
For example, List.size or List.clear. In fact, Class<?> is so often
used because most of the methods in Class<T> do not depend on T.
The unbounded wildcard <?> appears to mean "anything", and so using an unbounded wildcard seems equivalent to using a raw type. Indeed, the compiler seems at first to agree with this assessment:
import java.util.ArrayList;
import java.util.List;
public class UnboundedWildcards1 {
static List list1;
static List<?> list2;
static List<? extends Object> list3;
static void assign1(List list) {
list1 = list;
list2 = list;
// list3 = list; // Warning: unchecked conversion
// Found: List, Required: List<? extends Object>
}
static void assign2(List<?> list) {
list1 = list;
list2 = list;
list3 = list;
}
static void assign3(List<? extends Object> list) {
list1 = list;
list2 = list;
list3 = list;
}
public static void main(String[] args) {
assign1(new ArrayList());
assign2(new ArrayList());
// assign3(new ArrayList()); // Warning:
// Unchecked conversion. Found: ArrayList
// Required: List<? extends Object>
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
// Both forms are acceptable as List<?>:
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
}
There are many cases like the ones you see here where the compiler could care less whether you use a raw type or <?>. In those cases, <?> can be thought of as a decoration; and yet it is valuable because, in effect, it says, "I wrote this code with Java generics in mind, and I don't mean here that I'm using a raw type, but that in this case the generic parameter can hold any type."
A second example shows an important use of unbound wildcards. When you are dealing with multiple generic parameters, it's sometimes important to allow one parameter to be any type while establishing a particular type for the other parameter:
import java.util.HashMap;
import java.util.Map;
public class UnboundedWildcards2 {
static Map map1;
static Map<?, ?> map2;
static Map<String, ?> map3;
static void assign1(Map map) {
map1 = map;
}
static void assign2(Map<?, ?> map) {
map2 = map;
}
static void assign3(Map<String, ?> map) {
map3 = map;
}
public static void main(String[] args) {
assign1(new HashMap());
assign2(new HashMap());
// assign3(new HashMap()); // Warning:
// Unchecked conversion. Found: HashMap
// Required: Map<String,?>
assign1(new HashMap<String, Integer>());
assign2(new HashMap<String, Integer>());
assign3(new HashMap<String, Integer>());
}
}
But again, when you have all unbounded wildcards, as seen in Map<?,?>, the compiler doesn't seem to distinguish it from a raw Map. In addition, the first example shows that the compiler treats List<?> and List<? extends Object> differently.
For more information you are welcome to read Bruce Eckel's book: Thinking in Java
Recently I was refactoring a generic method when I got into generic casting issues I cannot explain. Finally I realized I could do without the T type altogether (just inline it myself), but I'm still curious as to why the convert fail.
I created this minimal example to illustrate the issue.
Can someone explain me why the convert fails and the workaround works?
public <K, T extends List<K>> void castLists(List<T> list, K kForBinging) {
Map<Integer, List<T>> map = mapSizeToList(list);
// Type mismatch: cannot convert from Map<Integer,List<T>> to Map<Integer,List<List<K>>>
// Map<Integer, List<List<K>>> expandedMap = map;
// Added after accepting answer, legal assignment:
Map<Integer, ? extends List<? extends List<K>>> expandedMap = map;
// Originally proposed 'work around'
Map<Integer, ?> lessSpecific = map;
#SuppressWarnings("unchecked")
Map<Integer, List<List<K>>> canCast = (Map<Integer, List<List<K>>>)lessSpecific;
// ...
}
public <A> Map<Integer, List<A>> mapSizeToList(List<A> list) {
Map<Integer, List<A>> map = Maps.newHashMap();
// ...
return map;
}
I believe you need Covariance with generics before you can do such things. This doesnt seem to be supported by Java.
i.e in Java, if T is a subtype of List<K>, it does NOT imply that List<T> is a subtype of List<List<K>> or that Map<Integer,List<T>> is a subtype of Map<Integer, List<List<K>>>. This is why the assignment errors out.
Covariance would allow you to do this because with it, if template parameters have a subclass-superclass relationship, the defined classes will also have the exact same relationship. This would make this assignment possible. Scala (among other (functional programming?) languages) supports covariance and its complement contravariance.
I am trying to make a method call like this,
public class GenericsTest<T> {
public static <T> Map<String, T> createMap(Class<? extends Map<String, T>> clazz) {
return null;
}
public static void main(String[] argv) {
Map<String, Integer> result = createMap(TreeMap.class);
}
}
But I am getting this error,
<T>createMap(java.lang.Class<? extends java.util.Map<java.lang.String,T>>) in test.GenericsTest<T> cannot be applied to (java.lang.Class<java.util.TreeMap>)
How to fix this problem?
Map<String, Integer> instance = new TreeMap<String, Integer>();
#SuppressWarnings("unchecked")
Map<String, Integer> map =
createMap((Class<? extends Map<String, Integer>>)instance.getClass());
map.put("x", 1);
System.out.println("THIS IS x: " + map.get("x"));
This will appropriately print out 1. The implementation of the method is most likely
try
{
return clazz.newInstance();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
A better implementation of their API would be for them to ask you for the type, T, and for them to give back a Map of their choosing instead of asking you for all of the details. Otherwise, as long as they are not filling in the Map with any data, you can instantiate a Map with the generic type argument yourself like so:
public static <T> Map<String, T> getMap()
{
return new TreeMap<String, T>();
}
You can then access that without a warning:
// note the lack of type arguments, which are inferred
Map<String, Integer> instance = getMap();
// alternatively, you could do it more explicitly:
// Map<String, Integer> instance = ClassName.<Integer>getMap();
There's really no reason for them to ask you for the Class type of your Map except to give you back an exact match to the implementation (e.g., if you stick in a HashMap, then you will get back a HashMap, and if you stick in a TreeMap, then you will get back a TreeMap). However, I suspect that the TreeMap will lose any Comparator that it was constructed with, and since that is an immutable (final) field of TreeMap, then you cannot fix that; that means that the Map is not the same in that case, nor is it likely to be what you want.
If they are filling in the Map with data, then it makes even less sense. You could always pass in an instance of a Map to fill, or have them return a Map that you can simply wrap (e.g., new TreeMap<String, Integer>(instance);), and they should know which Map offers the most utility to the data.
While f1 does compile, the very similar f2 won't and I just cant explain why.
(Tested on Intellij 9 and Eclipse 3.6)
And really I thought I was done with that kind of question.
import java.util.*;
public class Demo {
public List<? extends Set<Integer>> f1(){
final List<HashSet<Integer>> list = null;
return list;
}
public List<List<? extends Set<Integer>>> f2(){
final List<List<HashSet<Integer>>> list = null;
return list;
}
}
List<List<HashSet<Integer>>> is not assignable to List<List<? extends Set<Integer>>> for the same reason List<HashSet<Integer>> would not be assignable to List<Set<Integer>>.
You can get it to compile by changing this:
public List<List<? extends Set<Integer>>> f2(){
into this:
public List<? extends List<? extends Set<Integer>>> f2(){
The reason your code didn't compile, and why the other example I gave (ie: "List<HashSet<Integer>> would not be assignable to List<Set<Integer>>") is that Java generics are not covariant.
The canonical example is that even if Circle extends Shape, List<Circle> does not extend List<Shape>. If it did, then List<Circle> would need to have an add(Shape) method that accepts Square objects, but obviously you don't want to be able to add Square objects to a List<Circle>.
When you use a wildcard, you're getting a type that slices away certain methods. List<? extends Shape> retains the methods that return E, but it doesn't have any of the methods that take E as a parameter. This means you still have the E get(int) method, but add(E) is gone. List<? extends Shape> is a super-type of List<Shape> as well as List<Circle>, List<? extends Circle>, etc. (? super wildcards slice the other way: methods that return values of the type parameter are removed)
Your example is more complicated because it has nested type parameters, but it boils down to the same thing:
List<HashSet<Integer>> is a sub-type of List<? extends Set<Integer>>
Because generics are not covariant, wrapping the two types in a generic type (like List<...>) yields a pair of types that no longer have the sub/super-type relationship. That is, List<List<HashSet<Integer>>> is not a sub-type of List<List<? extends Set<Integer>>>
If instead of wrapping with List<...> you wrap with List<? extends ...> you'll end up with the original relationship being preserved. (This is just a rule of thumb, but it probably covers 80% of the cases where you'd want to use wildcards.)
Note that trashgod and BalusC are both correct in that you probably don't want to be returning such a weird type. List<List<Set<Integer>>> would be a more normal return type to use. That should work fine as long as you're consistent about always using the collection interfaces rather than the concrete collection classes as type parameters. eg: you can't assign a List<ImmutableSet<Integer>> to a List<Set<Integer>>, but you can put ImmutableSet<Integer> instances into a List<Set<Integer>>, so never say List<ImmutableSet<Integer>>, say List<Set<Integer>>.
"Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code."—Joshua Bloch, Effective Java Second Edition, Chapter 5, Item 28.
This is too long to fit in a comment. I just wanted to say that it makes no sense to declare it that way. You can also just do the following:
public List<List<Set<Integer>>> f2() {
List<List<Set<Integer>>> list = new ArrayList<List<Set<Integer>>>();
List<Set<Integer>> nestedList = new ArrayList<Set<Integer>>();
list.add(nestedList);
Set<Integer> set = new HashSet<Integer>();
nestedList.add(set);
return list;
}
Works as good. I see no point of using ? extends SomeInterface here.
Update: as per the comments, you initially wanted to solve the following problem:
public List<Map<Integer, Set<Integer>>> getOutcomes() {
Map<HashSet<Integer>, Integer> map = new HashMap<HashSet<Integer>, Integer>();
List<Map<Integer, Set<Integer>>> outcomes = new ArrayList<Map<Integer, Set<Integer>>>();
for (Map.Entry<HashSet<Integer>, Integer> entry : map.entrySet()) {
outcomes.add(asMap(entry.getValue(), entry.getKey()));
// add() gives compiler error: The method add(Map<Integer,Set<Integer>>)
// in the type List<Map<Integer,Set<Integer>>> is not applicable for
// the arguments (Map<Integer,HashSet<Integer>>)
}
return outcomes;
}
public <K, V> Map<K, V> asMap(K k, V v) {
Map<K, V> result = new HashMap<K, V>();
result.put(k, v);
return result;
}
This can just be solved by declaring the interface (Set in this case) instead of implementation (HashSet in this case) as generic type. So:
public List<Map<Integer, Set<Integer>>> getOutcomes() {
Map<Set<Integer>, Integer> map = new HashMap<Set<Integer>, Integer>();
List<Map<Integer, Set<Integer>>> outcomes = new ArrayList<Map<Integer, Set<Integer>>>();
for (Map.Entry<Set<Integer>, Integer> entry : map.entrySet()) {
outcomes.add(asMap(entry.getValue(), entry.getKey()));
// add() now compiles fine.
}
return outcomes;
}
In future problems, try to ask how to solve a particular problem, not how to achieve a particular solution (which in turn may not be the right solution after all).
I have a method that returns an instance of
Map<String, List<Foo>> x();
and another method that returns an instance of
Map<String, Collection<Foo>> y();
Now if I want to dynamically add one of this Maps in my field, how can I write the generics for it to work?
ie:
public class Bar {
private Map<String, ? extends Collection<Foo>> myMap;
public void initializer() {
if(notImportant) myMap = x(); //OK
else myMap = y(); // !OK (Need cast to (Map<String, ? extends Collection<Foo>>)
}
}
Now is it ok that I cast to the signature even though the y() is declared as being Collection?
If it is not ok to cast, can I somehow write this (Collection OR List)
I mean, List is a Collection, so it should somehow be possible.
private Map<String, Collection<Foo> | List<Foo>>> myMap;
The way you did it with ? extends Collection is fine. You can't have something like OR since if you did you wouldn't know what it is you're getting back if you do myMap.get("someString"); you can't do List|Collection someVariable = myMap.get("someString"), you have to choose one, and if you choose Collection it's the same as using ? extends, if you choose List, you'll end up in all sort of trouble if the object in the map is actually a Set (which is also a collection), not a list, and you try calling methods that only List has (like indexOf). As for the reason you need to use ? extends is because Map<String, List> does not extend Map<String, Collection> even though List extends Collection.
You should take note though, that using ? extends Collection will only let you get values from the map, since then it's sure that what you get is a Collection (or child of Collection), but if you try to put something in the map, you won't be able to (since myMap may be Map<String, Set>, but since you only see it as Map<String, ? extends Collection> you might try to put a List in it which wouldn't be ok)
I'm not sure what your problem is. This code (essentially your code) compiles just fine.
import java.util.*;
public class Generic {
static class Foo {};
static Map<String, List<Foo>> x() {
return null;
}
static Map<String, Collection<Foo>> y() {
return null;
}
static Map<String, ? extends Collection<Foo>> myMap;
public static void main(String[] args) {
myMap = x();
myMap = y();
myMap = new HashMap<String,SortedSet<Foo>>();
for (Collection<Foo> value : myMap.values());
}
}
You can NOT, however, do something like List<Integer|String>. Java generics type bounds just doesn't work like that.