2015-11-08 43 views
0

我得到用下面的代码的错误(摘录如下所述):Map.put使用泛型

public class MyClass { 
    private Map<String, Subclass1> mapToSubclass1; 
    private Map<String, Subclass2> mapToSubclass2; 

    public void update(
     final boolean updatesAreSubclass1, 
     final List<? extends Superclass> updates) { 

    Map<String, Superclass> mapToUpdate; 
    if (updatesAreSubclass1) { 
     mapToUpdate = mapToSubclass1; 
    } else { 
     mapToUpdate = mapToSubclass2; 
    } 


    updates.stream().forEach((entity) -> { 
     mapToUpdate.put(entity.getId(), entity); 
    }); 
    } 
} 

其中Subclass1Subclass2延伸Superclass,和Superclass提供public String getId();

正如我写的,在尝试定义mapToUpdate - Incompatible types. Required: Map<String, foo.bar.Superclass>, Found: Map<String, foo.bar.Subclass1>(或子类2,在else子句中)时会出错。

如果我改变mapToUpdate到Map<String, ? extends Superclass>,我试图put当得到一个错误 - Wrong 2nd argument type. Found: 'foo.bar.Superclass', required '? extends foo.bar.Superclass'

认为这是协方差的概念做的,但我不知道如何解决问题。我想到了,没有满意的一对夫妇的解决方案:

  • 我应该需要两个update方法,每个子类(如果有两个以上这很快就会变得混乱)?
  • 我是否应该将put移至if (updatesAreSubclass1)条款中,并将updates转换为相应的List<Subclass>
+0

我不知道我是否理解正确,但不是可以将子类映射投影到mapToUpdate吗? mapToUpdate =(HashMap )(Map)mapToSubclass1; – Juan

回答

2

这是一个解决方案,可以使用无限数量的可能子类,因为据我可以告诉你只是创建一个类加Id - >超类的映​​射。

private Map<Class,Map<String,Superclass>> map = new HashMap<>(); 
void update(List<? extends Superclass> l) { 
    l.stream().forEach(o -> put(o)); 
} 

public void put(Superclass obj) { 
    String id = obj.getId(); 
    Map<String,Superclass> submap = map.get(obj.getClass()); 
    if(null == submap) { 
     submap = new HashMap<>(); 
     map.put(obj.getClass(), submap); 
    } 
    submap.put(id, obj); 
} 

public Superclass get(Class clss, String id) { 
    return Optional.ofNullable(map) 
      .map(m -> m.get(clss)) 
      .map(m2 -> m2.get(id)) 
      .orElse(null); 
} 
+0

梦幻般的,像一个魅力工作 - 谢谢! – scubbo

1

我最好的办法来解决这个问题是创建两个更新方法。一个用于Subclass1,另一个用于Subclass2。原因很简单,最好有两个单一的方法做一件事,比一个带有布尔参数的方法做两件事。

此代码看起来很不错,而且更具可测性。

public void update1(final List<Subclass1> updates) { 
    updates.stream().forEach((entity) -> { 
     mapToSubclass1.put(entity.getId(), entity); 
    }); 
} 

public void update2(final List<Subclass2> updates) { 
    updates.stream().forEach((entity) -> { 
     mapToSubclass2.put(entity.getId(), entity); 
    }); 
} 
1

Check this

正常继承不仿制工作。所以,Map<String, Subclass1>并不从Map<String, SuperClass>延伸。

您的选择是明确地投下对象

if (updatesAreSubclass1) { 
    updates.stream().forEach((entity) -> { 
     mapToSubclass1.put(entity.getId(), (SubClass1) entity); 
    }); 
} else { 
    updates.stream().forEach((entity) -> { 
     mapToSubclass2.put(entity.getId(), (SubClass2) entity); 
    }); 
} 
1

你的方法在继承方面没有很多意义。你有两个单独的子类地图,并希望在其中的任何一个中添加超类实例。我会建议想一个更合适的方式来处理这个用例。

但是,如果你想保留的事情会是这样,这将这样的伎俩:

public void update(
      final boolean updatesAreSubclass1, 
      final List<? extends Superclass> updates) { 
    updates.stream().forEach((entity) -> { 
    if(updatesAreSubclass1) 
     mapToSubclass1.put(entity.getId(), (Subclass1) entity); 
    else 
     mapToSubclass2.put(entity.getId(), (Subclass2) entity); 
    }); 
} 

您不能存储在为没有明确铸造一个子类中定义的映射Superclass对象。这应该会让你认为你的实现可能有问题。

1

你不能做到这一点:

mapToUpdate = mapToSubclass1; 

,因为你的代码可以去非Subclass1对象添加到mapToUpdate,编译器将无法对其进行标记(即,它止跌”不能提供类型安全)。

解决此问题的一个方法是告诉编译器“我知道我在做什么”,并且不会为您的mapToUpdate变量使用泛型。就像这样:

@SuppressWarnings("unchecked") 
public void update(final boolean updatesAreSubclass1, 
     final List<? extends Superclass> updates) { 

    if (updates.size() == 0) { 
     return; 
    } 

    Map mapToUpdate; 
    if (updatesAreSubclass1) { 
     mapToUpdate = Collections.checkedMap(mapToSubclass1, Integer.class, 
       Subclass1.class); 
    } else { 
     mapToUpdate = Collections.checkedMap(mapToSubclass2, Integer.class, 
       Subclass2.class); 
    } 

    updates.stream().forEach(
      (entity) -> { 
       System.out.println("Adding..." + entity.toString() 
         + " to map " + mapToUpdate.toString()); 
       mapToUpdate.put(entity.getId(), entity); 
      }); 
} 

需要提醒的是,你真的需要知道你在做什么,因为如果你调用updateupdatesAreSubclass1 = true,当列表是不是真的Subclass1对象的列表,你会运行时获得ClassCastException。我们使用Collections.checkedMap。如果你不这样做,你不会得到一个例外,但是你会在mapToSubclass1地图中得到Subclass2对象 - 更糟的是,对吧?

+0

如果'update'检查列表,而不是依靠调用者告诉它它正在获取什么类型的列表,这种方法会更安全。 –