如何安全地将 java 对象转换为泛型集合?
How to cast java Object to generic collection safely?
我可以安全地转换简单的 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)可以帮助进行此类转换?
确实没有什么好办法。天真的方法是
- 检查它是 list/set/collection
- 勾选所有项目
Entry
s
- 检查每个
Entry
的所有键和值都是String
s
例如
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);
然后你会发现这个cast还是不安全的。您只能使用当前状态,因此不能对未来状态做出任何保证。
您可以通过以下几种方式解决此问题:
- 检查后深拷贝整个集合。但是在复制之前检查之后的竞争条件呢?您将不得不使用锁。
- 使地图不可变
如果它是一个集合但当前为空怎么办?在这种情况下,您无法对项目进行任何检查。
如您所见,与这样的演员合作是一项非常艰苦的工作,并且有很多地方可能会出错。你最好的选择是避免首先进行这样的转换。
我可以安全地转换简单的 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)可以帮助进行此类转换?
确实没有什么好办法。天真的方法是
- 检查它是 list/set/collection
- 勾选所有项目
Entry
s - 检查每个
Entry
的所有键和值都是String
s
例如
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);
然后你会发现这个cast还是不安全的。您只能使用当前状态,因此不能对未来状态做出任何保证。
您可以通过以下几种方式解决此问题:
- 检查后深拷贝整个集合。但是在复制之前检查之后的竞争条件呢?您将不得不使用锁。
- 使地图不可变
如果它是一个集合但当前为空怎么办?在这种情况下,您无法对项目进行任何检查。
如您所见,与这样的演员合作是一项非常艰苦的工作,并且有很多地方可能会出错。你最好的选择是避免首先进行这样的转换。