我第一次使用@CompileStatic
,并且对于Groovy的地图构造器在这种情况下如何工作感到困惑。
@CompileStatic
class SomeClass {
Long id
String name
public static void main(String[] args) {
Map map = new HashMap()
map.put("id", 123L)
map.put("name", "test file")
SomeClass someClass1 = new SomeClass(map) // Does not work
SomeClass someClass2 = map as SomeClass // Works
}
}
鉴于上面的代码,我在尝试编译时看到以下错误
Groovyc: Target constructor for constructor call expression hasn't been set
如果删除了@CompileStatic
,则两个构造函数都能正常工作。
任何人都可以解释为什么new SomeClass(map)
无法使用@CompileStatic
进行编译?还有一个可能的补充,为什么map as SomeClass
仍有效?
答案 0 :(得分:3)
Groovy确实不给你一个“Map-Constructor”。构造函数 你班上写的是你的课。如果没有(就像你的情况一样), 然后是默认的c'tor。
但是如果你使用所谓的map c'tor(或者更确切地说是它),会发生什么 “按地图构建对象”)? groovy的一般方法是这样的:
SomeClass(Long id, String name)
)如果您取消代码(使用@CompileDynamic
(默认值)),您会看到
施工由CallSite.callConstructor(Object,Object)
处理,
归结为此code area。
现在通过地图引入这个构造的版本,这是更熟悉的
对于常规的groovyist:
SomeClass someClass3 = new SomeClass(id: 42L, name: "Douglas")
。
使用代码的动态版本,实际上反汇编
很像你的地图代码。 Groovy从param和s创建一个映射
将其发送到callConstructor
- 所以这实际上是相同的代码路径
采取(减去隐式地图创建)。
现在忽略“cast-case”,因为它实际上对于静态和
动态:它将基本上发送到ScriptBytecodeAdapter.asType
在任何情况下都会为您提供动态行为。
现在@CompileStatic
案例:如你所见,你的电话是
c'tor的显式地图不再有效。这是因为事实
从来没有一个明确的“地图 - 人”。班级还在
只有它的默认c'tor和静态编译groovyc
现在可以
与那里的事物一起工作(如果在这种情况下不存在的话,则不工作)。
new SomeClass(id: 42L, name: "Douglas")
怎么样?这仍然有效
用静态编译!原因是groovyc
展开了这个
为了你。如您所见,这简单归结为def o = new SomeClass();
o.setId(42); o.setName('Douglas')
:
new #2 // class SomeClass
dup
invokespecial #53 // Method "<init>":()V
astore_2
ldc2_w #54 // long 42l
dup2
lstore_3
aload_2
lload_3
invokestatic #45 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
invokevirtual #59 // Method setId:(Ljava/lang/Long;)V
aconst_null
pop
pop2
ldc #61 // String Douglas
dup
astore 5
aload_2
aload 5
invokevirtual #65 // Method setName:(Ljava/lang/String;)V
答案 1 :(得分:2)
正如CompileStatic
文档所说:
实际上会确保推断出的方法 名为 的名称将在运行时有效调用 。这个注释转向 将Groovy编译器转换为静态编译器,其中所有方法调用都是 在编译时解决,生成的字节码确保 发生这种情况
结果,在静态编译中搜索带有Map参数的构造函数,以便#34; 在编译时解析它&#34;,但是找不到它,因此有编译错误:
Target constructor for constructor call expression hasn't been set
添加这样的构造函数解决了@CompileStatic
注释的问题,因为它在编译时解决了:
import groovy.transform.CompileStatic
@CompileStatic
class SomeClass {
Long id
String name
SomeClass(Map m) {
id = m.id as Long
name = m.name as String
}
public static void main(String[] args) {
Map map = new HashMap()
map.put("id", 123L)
map.put("name", "test file")
SomeClass someClass1 = new SomeClass(map) // Now it works also
SomeClass someClass2 = map as SomeClass // Works
}
}
如果您想深入挖掘,可以查看StaticCompilationVisitor
。
关于行
SomeClass someClass2 = map as SomeClass
您正在使用asType()
的Groovy's GDK java.util.Map
方法,因此即使在静态编译中它也会在运行时解决:
使用地图关键字作为公共关键字,将此地图强制转换为给定类型 方法名称和值作为实现。通常是价值 将是一个闭包,其行为类似于方法实现。