假设我必须关注sed -i '/volume:/d' '' ./docker-compose.yaml
:
set
如果我要使用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)
,则在尝试使用Collections.unmodifiableSet()
方法时会引发异常,但add()
并非如此。为什么?
根据documentation,它应该引发错误:
返回指定集合的不可修改视图。这个方法 允许模块为用户提供对内部的“只读”访问权限 集合。查询操作对“返回的集合”的读取 到指定的集合,然后尝试修改 返回的集合,无论是直接的还是通过其迭代器的,都会导致 UnsupportedOperationException。
所有代码都是使用Groovy 2.5.2编写的
答案 0 :(得分:1)
简短答案:可以向此集合添加Peach
,因为Groovy确实将Collection
的类型从动态转换为Set
类型,因此fruitSet
变量的类型不是{{ 1}},但Collections$UnmodifiableCollection
。
看看这个简单的示例类:
LinkedHashSet
在像Java这样的静态编译语言中,以下行将引发编译错误:
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<String> fruitSet = Collections.unmodifiableCollection(fruits)
强制转换为Collection
(由于Set
扩展了Set
,它的作用方向相反)。现在,由于Groovy在设计上是一种动态语言,因此如果左侧的类型无法访问右侧返回的类型,它将尝试转换为左侧的类型。如果编译此代码,请执行一个Collection
文件,然后对其进行反编译,您将看到类似以下的内容:
.class
有趣的一行如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class DynamicGroovyCastExample implements GroovyObject {
public DynamicGroovyCastExample() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
Set fruits = (Set)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(HashSet.class), Set.class);
var1[1].call(fruits, "Apple");
var1[2].call(fruits, "Grapes");
var1[3].call(fruits, "Orange");
Set fruitSet = (Set)ScriptBytecodeAdapter.castToType(var1[4].call(Collections.class, fruits), Set.class);
var1[5].callStatic(DynamicGroovyCastExample.class, fruitSet);
var1[6].call(fruitSet, "Peach");
var1[7].callStatic(DynamicGroovyCastExample.class, fruitSet);
}
}
Groovy看到您已将Set fruitSet = (Set)ScriptBytecodeAdapter.castToType(var1[4].call(Collections.class, fruits), Set.class);
的类型指定为fruitSet
,并且由于右侧表达式返回了Set<String>
,因此它试图将其强制转换为所需的类型。现在,如果我们跟踪接下来会发生什么,我们将发现Collection
转到:
ScriptBytecodeAdapter.castToType()
来源:src/main/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java#L253
这就是为什么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);
}
// .....
}
是fruitSet
而不是LinkedHashSet
的原因。
当然,它对于Collections$UnmodifableCollection
来说也很好,因为在这种情况下不需要强制转换-Collections.unmodifiableSet(fruits)
实现Collections$UnmodifiableSet
,因此不涉及动态强制转换。
如果不需要任何Groovy动态功能,请使用静态编译来避免Groovy的动态性质出现问题。如果仅通过在类上添加Set
注释来修改此示例,则该示例将无法编译,因此我们会提早发出警告:
第二,始终使用有效类型。如果该方法返回@CompileStatic
,则将其分配给Collection
。您可以在运行时中进行动态强制转换,但是您必须意识到它可能带来的后果。
希望有帮助。