2015-11-08 141 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'檢查列表,而不是依靠調用者告訴它它正在獲取什麼類型的列表,這種方法會更安全。 –