Groovy 为什么Collections.unmodifiableCollection允许您更改集合?
假设我必须按照Groovy 为什么Collections.unmodifiableCollection允许您更改集合?,groovy,collections,unmodifiable,Groovy,Collections,Unmodifiable,假设我必须按照设置: Set<String> fruits = new HashSet<String>() fruits.add("Apple") fruits.add("Grapes") fruits.add("Orange") Set<String> unmodifiableFruits = Collections.unmodifiableSet(new HashSet<String>(fruits)) unmodifiableFruits.
设置:
Set<String> fruits = new HashSet<String>()
fruits.add("Apple")
fruits.add("Grapes")
fruits.add("Orange")
Set<String> unmodifiableFruits = Collections.unmodifiableSet(new HashSet<String>(fruits))
unmodifiableFruits.add("Peach") // -- Throws UnsupportedOperationException
Set<String> fruitSet = Collections.unmodifiableCollection(fruits)
fruitSet.add("Peach")
println(fruitSet)
Set fruits=new HashSet()
水果。添加(“苹果”)
水果。添加(“葡萄”)
水果。添加(“橙色”)
Set unmodifiableFruits=Collections.unmodifiableSet(新HashSet(fruits))
不可修改的水果。添加(“Peach”)/--抛出UnsupportedOperationException
集合水果集合=集合。不可修改的集合(水果)
水果集。添加(“桃子”)
println(果集)
如果我要使用Collections.unmodifiableSet()
当我尝试使用add()
方法时,它会抛出一个异常,但Collections.unmodifiableCollection()的情况并非如此。为什么?
根据,它应该抛出一个错误:
返回指定集合的不可修改视图。这种方法
允许模块为用户提供对内部数据库的“只读”访问
收藏。对返回的集合“读取”的查询操作
到指定集合,并尝试修改
返回的集合,无论是直接的还是通过其迭代器,都会导致
不支持操作异常。
所有代码都是使用Groovy 2.5.2编写的。简短回答:向这个集合添加Peach
是可能的,因为Groovy会将collection
动态转换为Set
类型,所以foultset
变量不是Collections$UnmodifiableCollection
类型,而是LinkedHashSet
看看这个简单的示例类:
class DynamicGroovyCastExample {
static void main(String[] args) {
Set<String> fruits = new HashSet<String>()
fruits.add("Apple")
fruits.add("Grapes")
fruits.add("Orange")
Set<String> fruitSet = Collections.unmodifiableCollection(fruits)
println(fruitSet)
fruitSet.add("Peach")
println(fruitSet)
}
}
有趣的一行如下:
Set fruitSet = (Set)ScriptBytecodeAdapter.castToType(var1[4].call(Collections.class, fruits), Set.class);
Groovy发现您已经将水果集
的类型指定为集合
,并且因为右侧表达式返回一个集合
,所以它尝试将其强制转换为所需的类型。现在,如果我们跟踪接下来发生的事情,我们将发现ScriptBytecodeAdapter.castToType()
转到:
private static Object continueCastOnCollection(Object object, Class type) {
int modifiers = type.getModifiers();
Collection answer;
if (object instanceof Collection && type.isAssignableFrom(LinkedHashSet.class) &&
(type == LinkedHashSet.class || Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
return new LinkedHashSet((Collection)object);
}
// .....
}
资料来源:
这就是为什么foultset
是LinkedHashSet
而不是Collections$unmodifabelecollection
的原因
当然,它对于集合非常有效。unmodifiableSet(fruits)
,因为在这种情况下不需要强制转换-Collections$unmodifiableSet
实现Set
,因此不涉及动态强制转换
如何防止类似情况?
如果您不需要任何Groovy动态特性,请使用静态编译来避免Groovy的动态特性出现问题。如果我们仅通过在类上添加@CompileStatic
注释来修改此示例,它将不会编译,并且我们将收到早期警告:
其次,始终使用有效类型。如果该方法返回集合
,请将其分配给集合
。您可以在运行时处理动态强制转换,但必须注意它可能带来的后果
希望能有所帮助。确实如此。@sp00m-在Groovy
中,它确实可以编译。我怀疑Groovy正在从unmodifiableCollection
返回的类型(java.util.Collections$unmodifiableCollection
就我的测试而言)隐式转换到Set
。很可能它正在创建一个新的、可修改的集合(java.util.LinkedHashSet
是在我的测试中分配给水果集的类型)。@ernest_k-这有点误导。文档中明确说明返回指定集合的不可修改视图。事实似乎并非如此。@ernest_k-在这种情况下,不可修改集
是更好的选择?因此,最好使用集合。不可修改集
?既然我知道我的集合是一个集合,那么调用它是最安全的方法,对吗?@Sveta-correct。静态编译不允许您将集合
分配给集合
,因此尽可能明确始终是一个好的选择。通知文档的编写者集合是否是一个好主意。不可修改的集合
页面无法准确描述该方法的功能?Set
页面来自Groovy,但它链接到Java的集合。对于任何读者来说,这可能会让人困惑。collection.unmodifiableCollection()
的文档是可以的,它会返回所描述的内容。您面临的问题是在不兼容的类型之间转换Groovy“特性”。右侧表达式Collections.unmodifiableCollection()
返回不可修改的集合,然后Groovy与左侧的类型进行比较,找到Set
,因为collection
无法强制转换为Set
它应用了ScriptBytecodeAdapter.castToType()
从给定的集合创建集合
。这只是由从类型Collection
到Set
的动态转换所支持的异常用法。问题是Groovy的Set
文档链接到Java关于集合的文档。unmodifiableCollection()
可能会让读者产生这样的想法Collections.unmodifiableCollection()
在Groovy和Java中的函数是相同的,但正如您所演示的,它不是。在Java中,Collections.unmodifiableCollection()
在尝试调用add()
时抛出异常,就像文档中解释的那样。在Groovy中,它不会这样做,正如您所演示的,它是从给定集合创建的Set
。
Set fruitSet = (Set)ScriptBytecodeAdapter.castToType(var1[4].call(Collections.class, fruits), Set.class);
private static Object continueCastOnCollection(Object object, Class type) {
int modifiers = type.getModifiers();
Collection answer;
if (object instanceof Collection && type.isAssignableFrom(LinkedHashSet.class) &&
(type == LinkedHashSet.class || Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
return new LinkedHashSet((Collection)object);
}
// .....
}