开发者

How do I properly implement a Map for Custom types using generics in nested classes

In short I'm trying to write an AbstractMap implementation and I encountered a syntax problem that I do not understand.

Consider the following two sketches (the identifiers with My before them are custom types).


(1) Not Working:

public class CustomMap extends AbstractMap<MyKey,MyValue> {
 开发者_如何学Python   private Set<Map.Entry<MyKey,MyValue>> data = null ;
    public static class MyMapEntry<K,V> implements Map.Entry<K,V> { 
        //...
    }
    public static class MyEntrySet<E> extends AbstractSet<E> {
        //...
    }
    public static class MyEntrySetIterator<E> implements Iterator<E> {
        //...
    }
    { // initializer block
        this.data = new MyEntrySet<MyMapEntry<MyKey,MyValue>>() ;
    }
    // (constructors and methods of the Map interface)

}

(2) Working:

public class CustomMap extends AbstractMap<MyKey,MyValue> {
    private Set<Map.Entry<MyKey,MyValue>> data = null ;
    public static class MyMapEntry<K,V> implements Map.Entry<K,V> { 
        //...
    }
    public static class MyEntrySet<E> extends AbstractSet<E> {
        //...
    }
    public static class MyEntrySetIterator<E> implements Iterator<E> {
        //...
    }
    { // initializer block
        this.data = new MyEntrySet<Map.Entry<MyKey,MyValue>>() ;
    }
    // (constructors and methods of the Map interface)

}

The only difference is in the way I instantiated this.data (the Set backing the Map): apparently the compiler does not accept an instance of Map.Entry (such as MyMapEntry) as the parameterized type of MyEntrySet.

The compiler complains about "incompatible types".

I wonder why.

Please post comments in case I need to elaborate, TIA,

FK82


That's why:

MyEntrySet<MyMapEntry<MyKey,MyValue>>() mySet = 
   new MyEntrySet<MyMapEntry<MyKey,MyValue>>();

this.data = mySet; // Imagine that this cast is possible
this.data.add(new SomeOtherMapEntry());

MyMapEntry<MyKey, MyValue> e = 
    mySet.iterator().next(); // Unexpected ClassCastException!

MyEntrySet<MyMapEntry<MyKey,MyValue>> is a Set that can contain only instances of MyMapEntry<MyKey,MyValue>. Set<Map.Entry<MyKey,MyValue>> can contain arbitrary instances of Map.Entry<MyKey,MyValue>. Cast between them would violate type safety.


It is common mistake about generics, it is conceptually difficult to adapt yourself that List<Child> is not assignable to List<Parent> , where Child is subtype of Parent.


I'll expand on the other answers with a simpler example.

Consider

List<String> strings = new ArrayList<String>();
List<Object> objects = strings; // this is what you are doing in your constructor
objects.add("some string");

objects has declared that it will only contain Objects, and strings has declared that it will only contain Strings. So surely assigning strings to objects should be fine as all Strings are Objects? We're even allowed to add Strings to objects without a problem.

However, what about this situation.

objects.add(new Object());

Since objects is just strings, we are adding an Object to strings, and thus breaking its declaration that it will only contain Strings.


This is not your fault. In the Map API

    Set<Map.Entry<K,V>> entrySet() 

A more sophisticated version should be

    Set<? extends Map.Entry<K,V>> entrySet() 

So that MyEntrySet<MyMapEntry> could work just fine.


But I like the 1st version. I'm so sick of these stupid wildcards everywhere, it makes me want to puke.

There was a much better syntax, used by all research papers that Java Generics was based upon

    Set<+Map.Entry<K,V>> entrySet() 

Some smartass in Sun don't believe that java programmers can handle it, so we now have this much more fugly, insane syntax.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜