我可以通过Key在HashMap中实现类型安全吗?

时间:2012-11-09 12:00:09

标签: java generics map

以下场景要解决:我需要创建一个类,它将返回或存储一个Fruit of Fruits,具体取决于给定的id + classtype。所以我在质疑自己,Collection要使用,如果我能够将所有水果清单存储在同一个Collection中以获得简单性。我试图单独HashMap尝试存储所有列表,因为键值原则听起来很熟悉我的用例,只是它可能需要一些更复杂的键,因为我需要两个方面(在我的case id和fruits class),以识别价值。

所以我创建了一个自己的密钥对象:

class FruitKey
{
  int                     id;
  Class<? extends Fruits> type;

  public FruitKey( int id, Class<? extends Fruits> type )
  {
    this.id = id;
    this.type = type;
  }

  //equals, hashcode, etc.
}

在创建Map<FruitKey, List<? extends Fruits>>之后,推杆工作完美无缺,但当然当我尝试将我的FruitLists从地图中取出时,我无法保证特定的水果类型并且必须投射结果:

public static void main( String[] args )
{
  Map<FruitKey, List<? extends Fruits>> fruitMap = new HashMap<>();

  Apple oneApple = new Apple();
  List<Apple> manyApples = new ArrayList<>();
  manyApples.add( oneApple );

  fruitMap.put( new FruitKey( 1, Apple.class ), manyApples);

  //Any way to avoid typesafty and cast?
  List<Apple> myApples = (List<Apple>) fruitMap.get( new FruitKey( 1, Apple.class ) );
}

由于classtype已经包含在键中,我不知何故认为我可以将这些与map值连接起来,但不幸的是我没有成功,没有将地图专用于特殊的水果。你有没有机会避免我的类型/类别广播问题,或者可以推荐另一种收集类型来实现我的目标?当然我可以为每个水果创建一个地图,但由于那里有很多yummi水果,我试图避免它,因为我总是要在它们之间有所区别。

简而言之,这就是我想要的:

List<Apple> apples = SomeCollection.get( Key(id, Apple.class) );

同样存在Id的原因是,因为在同一个地图中可以有几个苹果列表,以及其他几个水果列表。

1 个答案:

答案 0 :(得分:1)

您可以使用异类容器。您仍然需要在容器内部进行转换,但由于通用方法和类的final,转换是安全的,您可以禁止警告。

这是安全的,因为通用put确保您永远不会放置任何会使演员失败的内容。

public final class SomeCollection {

     private Map<FruitKey<? extends Fruits>, List<? extends Fruits>> fruitMap = new HashMap<>();

     //safe cast
     @SuppressWarnings("unchecked")
     public <T extends Fruits> List<T> get(FruitKey<T> key)
     {
         return (List<T>) fruitMap.get(key);
     }

     public <T extends Fruits> void put(FruitKey<T> key, List<T> fruits)
     {
         fruitMap.put(key, fruits);
     }
}

键入FruitKey

class FruitKey<T extends Fruits>
{
   int                     id;
   Class<T> type;

   public FruitKey( int id, Class<T> type )
   {
      this.id = id;
      this.type = type;
   }     
}

修改

您还可以在Key<T>中创建一个内部类SomeCollection,其中包含一个FruitKey的购物篮以及相应的hashCodeequals。< / p>

private Map<Key<? extends Fruits>, List<? extends Fruits>> fruitMap = new HashMap<>();

//safe cast
@SuppressWarnings("unchecked")
public <T extends Fruits> List<T> getBasket(T fruit)
{
    Key<T extends Fruit> = new Key<T>(fruit.getBasketId());
    return (List<T>) fruitMap.get(key);
}
//or
//safe cast
@SuppressWarnings("unchecked")
public <T extends Fruits> List<T> getBasket(Class<T> klass, FruitKey key)
{
    Key<T extends Fruit> = new Key<T>(key);
    return (List<T>) fruitMap.get(key);
}

public <T extends Fruits> void addToBasket(T fruit, List<T> fruits)
{
    Key<T extends Fruit> = new Key<T>(fruit.getBasketId());
    fruitMap.put(key, fruits);
}