如何安全地将Java对象转换为泛型集合?

时间:2020-03-19 12:38:30

标签: java generics java-8 casting classcastexception

我可以通过执行类似的操作来安全地投射简单的Java对象:

Object str = "test";
Optional.of(str)
      .filter(value -> value instanceof String)
      .map(String.class::cast)
      .ifPresent(value -> System.out.println("Here is the value: " + value));

但是我该如何将对象填充对象转换为通用集合?

例如:

Object entries = /**/;
// this is unsafe
List<Entry<String, String>> subscriptions = (List<Entry<String, String>>) entries;

我应该如何处理这种情况?是否有任何库(例如ClassCastUtils之类的东西)可以帮助实现这种转换?

1 个答案:

答案 0 :(得分:1)

实际上并不是一个很好的方法。天真的方法是

  1. 检查它是一个列表/集合/集合
  2. 检查所有项目是否为<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <table> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> </tr> <tr> <td>7</td> <td>8</td> <td>9</td> </tr> </table>
  3. 检查每个Entry的所有键和值是否为Entry

例如

String

但是,即使这样也不能提供足够的保证。假设地图声明为:

Map<String, String> map = new HashMap<>();
map.put("a", "b");
Object entries = map.entrySet();

boolean safe = Optional.of(entries)
    .filter(Set.class::isInstance)
    .map(e -> (Set<?>) e)
    .filter(set -> set.stream().allMatch(Map.Entry.class::isInstance))
    .map(e -> (Set<Map.Entry<?, ?>>) e)
    .filter(set -> set.stream().allMatch(e -> e.getKey() instanceof String && e.getValue() instanceof String))
    .isPresent();

在我们检查时,这被认为是安全的。这是一个集合,所有条目当前都是字符串。但是,如果以后要使用其他一些代码来更改地图,例如

Map<String, Object> map = new HashMap<>();
map.put("a", "b");

然后您将意识到此强制转换仍然不安全。您只能使用当前状态,因此不能对将来的状态做任何保证。

您可以通过以下两种方法解决此问题:

  1. 检查后深复制整个集合。但是在复制之前检查之后的种族状况如何?您将必须使用锁。
  2. 使地图不可变

如果它是一个集合但当前为空,该怎么办?在这种情况下,您将无法对这些项目进行任何检查。

如您所见,使用这样的演员表是非常艰苦的工作,并且在很多地方您可能会出错。最好的选择是避免首先进行这样的投射。