Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
241 views
in Technique[技术] by (71.8m points)

java - Nested wildcards

Found fact about unbounded wildcards that is annoying me. For example:

public class Test {

      private static final Map<Integer, Map<Integer, String>> someMap = new HashMap<>();

      public static void main(String[] args) {
         getSomeMap();
      }

      static Map<?, Map<?, ?>> getSomeMap() {
         return someMap;  //compilation fails
      }
}

It fails, although works with Map<?, ?> or Map<?, Map<Integer, String>> return type.

Could someone tell me the exact reason? Thanks in advance.


Update

Seems that i understood and the simplest explanation for this question(omitting all these sophisticated rules), in my opinion, is the last note in Capture Conversion(link): Capture conversion is not applied recursively.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It is important to understand the implication of the wildcard types.

You already understood that you can assign your Map<Integer, Map<Integer, String>> to Map<?,??> as Map<?, ?> implies arbitrary types, unknown to whoever might have a reference of the declared type Map<?, ?>. So you can assign any map to Map<?, ?>.

In contrast, if you have a Map<?, Map<?, ?>> it has an unknown key type but the value type is not unknown. It’s Map<?,?> the type, recall the information above, that can be assigned with any map.

So, the following code is legal:

Map<?, Map<?, ?>> map=new HashMap<>();
map.put(null, Collections.<String,String>singletonMap("foo", "bar"));
map.put(null, Collections.<Double,Integer>singletonMap(42.0, 1000));
map.put(null, Collections.<Object,Boolean>singletonMap(false, true));

Here, we are putting a null key as we can’t put anything else for keys but arbitrary typed maps as values as that’s what a value type of Map<?, ?> implies: can be assigned from arbitrary maps. Note that by iterating over the entries we can also set other entries having non-null keys to arbitrary maps then.

So I’m quite sure that you don’t want to assign your Map<Integer, Map<Integer, String>> to a Map<?, Map<?, ?>> and discover arbitrary maps not being Map<Integer, String> as values afterwards and that you are quite happy that the compiler doesn’t allow this.

What you actually want to do is to assign your map to a type which has both, key and value type, unknown but still telling that your values are maps:

Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, ? extends Map<?, ?>> map=someMap;

In the generic type system Map<Integer, String> is a sub-type of Map<?, ?> so you can assign it to Map<?, ?> as well as ? extends Map<?, ?>. This sub-type relationship is not different than the relationship of String to Object. You can assign any String to a variable of type Object but if you have a Map<?,String> you can’t assign it to Map<?,Object> but only to Map<?, ? extends Object> for the same reason: the map shall continue to contain Strings as values rather than receiving arbitrary objects.

Note that you can workaround this limitation. You can say:

Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, Map<?, ?>> map=Collections.unmodifiableMap(someMap);

Since the map returned by unmodifiableMap does not allow any modifications, it allows widening the key and value types. The contained values are of the specified type (i.e. Map<?, ?>) when you query the map, but attempts to put in arbitrary map values, while not rejected by the compiler, will be rejected at runtime.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...