Heterogeneous Container with two element of same t

2019-01-29 01:26发布

问题:

I am reading Effective Java - Item 29. It talks about Heterogeneous container, in the example:

private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

public <T> void putFavirite(Class<T> type, T insance) {
    if(type == null) {
        throw new NullPointerException();
    }
    favorites.put(type, insance);

    ....
}

This pattern parametrises the key instead of values, so you are not limited to a single type, unlike:

 private Map<Integer, String> favorites ....    

My question is: what if there are two elements of same type that needed to be added to the Map, i.e. two String, is this pattern still useful?

回答1:

First, note that Item 29 is really about the general concept of using parameterized keys:

Sometimes, however, you need more flexibility. For example, a database row can have arbitrarily many columns, and it would be nice to be able to access all of them in a typesafe manner. Luckily there is an easy way to achieve this effect. The idea is to parameterize the key instead of the container.

The intent of this item is to demonstrate that you can use generics in more ways than just by parameterizing a type. The Heterogeneous Container pattern is simply an example of this technique. Admittedly, the item could make that point clearer with a better title like "Consider using parameterized methods to enforce type safety when working with arbitrary numbers of types".

The Heterogeneous Container pattern the item demonstrates is specifically for the case where you want to associate certain types with a particular instance of each type. Guava includes an implementation of this pattern with their ClassToInstanceMap type (more details). They also provide the more powerful TypeToInstanceMap that supports arbitrary generic types (e.g. List<String>) with an admittedly slightly more cumbersome API.

All of this is to say that there's nothing stopping you from creating a similarly structured class that supports multiple instances of a given type. We could easily take the ClassToInstanceMap API and create a ClassToInstanceMultimap type (extending Guava's Multimap API):

public interface ClassToInstanceMultimap<B> extends Multimap<Class<? extends B>, B> {
  /**
   * Returns the values the specified class is mapped to, or an empty collection if no
   * entries for this class is present. This will only return a value that was
   * bound to this specific class, not a value that may have been bound to a
   * subtype.
   */
  <T extends B> Collection<T> getInstances(Class<T> type);

  /**
   * Stores an entry mapping the specified class to the specified value. Does <i>not</i>
   *  associate this value with any of the class's supertypes.
   *
   * @return {@code true} if the method increased the size of the multimap, or
   * {@code false} if the multimap already contained the key-value pair and doesn't allow
   * duplicates
   */
  <T extends B> T putInstance(Class<T> type, T value);
}

Guava doesn't currently include such an interface, but the implementation of ClassToInstanceMap is pretty straightforward, so you could easily create your own ClassToInstanceMultimap implementations.



回答2:

If you put two strings the second will override the first one. So it is useful only if this behaviour is desired. If you want to store more objects under the same key you can use other containers, for example :

Map<Class<?>, List<Object>>

Or you can use MultiMap from Guava : http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multimap.html

Or you can use MultiMap from apache commons : http://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/MultiMap.html