【问题标题】:How to cast java Object to generic collection safely?如何安全地将 java Object 转换为泛型集合?
【发布时间】:2020-03-19 12:38:30
【问题描述】:

我可以通过执行以下操作安全地转换简单的 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)来帮助进行这种转换?

【问题讨论】:

  • 你不能。花哨的 lambdas 不会让擦除消失。
  • @user2357112supportsMonica 那么我应该如何处理这种情况呢?
  • @TyulpanTyulpan 你为什么会遇到这种情况?为什么你有Object
  • 你应该解释一下X,你目前在你的XY问题中询问了Y
  • 请不要只发布一个 sn-p 代码,而是一个正在运行的最小示例,这样我们就不会遇到 XY-Problems 推测您的用例。

标签: java generics java-8 casting classcastexception


【解决方案1】:

没有真正的好方法来做到这一点。天真的方法是

  1. 检查它是一个列表/集合/集合
  2. 检查所有项目都是Entrys
  3. 检查每个Entry的所有键和值是Strings

例如

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");

在我们检查时,这将被视为安全的。这是一个集合,所有条目当前都是字符串。但是,如果其他一些代码稍后会改变地图,例如

map.add("b", 123);

然后你会意识到这个演员阵容仍然不安全。您只能处理当前状态,因此无法对未来状态做出任何保证。

您可以通过以下几种方式解决此问题:

  1. 检查后深度复制整个集合。但是在复制之前检查之后的竞争条件呢?您必须使用锁。
  2. 使地图不可变

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

如您所见,与这样的演员一起工作是一项非常艰苦的工作,并且有很多地方可能会出错。最好的办法是避免一开始就进行这样的演员阵容。

【讨论】:

  • 你不需要锁,如果你将检查与强制转换结合起来,即将一个引用读入一个局部变量,检查它,强制转换,将它传递给新的构造函数/工厂不可变对象。但这比您发布的链更复杂。因此,在创建深层副本并捕获异常时乐观地进行转换可能会更简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-02
相关资源
最近更新 更多