簡體   English   中英

避免在Java中對嵌套映射進行未經檢查的轉換

[英]Avoiding unchecked casts of nested Maps in Java

我有一個Java數據結構,這是通過反序列化這個JSON得到的:

{
  'level1 value1': {
    'level2 value1': {
      'level3 value1': [ "25", "45", "78" ],
      // ...
      'level3 valueN': [ "59", "17", "42" ]
    },
    // ...
    'level2 valueN': {
      'level3 value1': [ "34", "89", "54" ],
      // ...
      'level3 valueN': [ "45", "23", "23" ]
    },
  },
  // ...
  'level1 valueN': {
    // ...
  }
}

在Java中,這變為:

Map<String, Map<String, Map<String, List<String>>>> data;

當然,級別的數量是任意的,所以我無法在變量聲明中嵌套集合。 我正在做的是這樣的:

void traverse(Map<String, ?> children) {
  for (Map.Entry<String, ?> node : data.entrySet()) {
    if (node.getValue() instanceof Map) {
      doSomethingWithNonLeafNode((Map<String, Map<String, ?>>) node);
    } else if (node.getValue() instanceof List) {
      doSomethingWithLeafNode((Map <String, List<String>>) node);
    }
  }
}


void doSomethingWithNonLeafNode(Map <String, Map<String ?>> node) {
  // do stuff
}


void doSomethingWithLeafNode(Map <String, List<String>> node) {
  // do stuff
}

這顯然是a)使用了一些未經檢查的演員,而b)是丑陋的。 我試圖定義新的類型來解決這個問題:

private interface Node extends Map<String, Map<String, ?>> {
}

private interface LeafNode extends Map<String, List<String>> {
}

// ...

    if (node.getValue() instanceof Map) {
      doSomethingWithNonLeafNode((Node) node);
    } else if (node.getValue() instanceof List) {
      doSomethingWithLeafNode((LeafNode) node);
    }

但是,這給了我一個運行時異常:

java.lang.ClassCastException: java.util.HashMap cannot be cast to com.foo.ReportDataProcessor$Node

我怎樣才能以干凈,無警告的方式做到這一點?

定義新類型對您沒有幫助,因為JSON反序列化器不了解您的Node和LeafNode接口,因此生成的具體集合對象無法實現它們。

由於您實際上並不事先知道映射的元素類型,因此不需要強制執行編譯時類型安全性:您只能依賴運行時類型檢查。 因此,仿制葯只會使您的生活變得復雜並使您的代碼變得更加丑陋,但並不會給您帶來任何好處。

所以我認為這里最差的解決方案是刪除泛型,在代碼中使用非泛型MapList類型。

沒有聯合類型,我認為你不能很好地做到這一點。

如果你考慮一下,你正在處理的Map實際上可以包含兩種類型的對象 - 下面級別的另一個映射,或者葉子節點的字符串列表。 因此,此映射的通用參數必須是映射和列表的一些常見超類型。 最后,當你檢測到你有一個葉節點並正在處理一個列表時,你將不得不進行投射。

為了更清晰的解決方案,您可能會忘記將Map作為容器,並創建一個對象以更好地表示樹

public class Node{
   String text;
   List<String> data;
   List<Node> children; 
}

您可以使用Map實現當前使用Map填充樹的方法。 如果Node的子項不為空,那么您不必考慮數據。

void traverse(Node node) {
  if(node.getChildren()!=null){
      doSomethingWithNonLeafNode(node);
      // Or for recursion if you need
      // List<Node> children=node.getChildren();
      // for(Node c:children){
      //    traverse(c);
      // }
  }else{
      doSomethingWithLeafNode(node);
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM