Groovy 的@CompileStatic 和映射构造函数
Groovy's @CompileStatic and map constructors
我是第一次使用 @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
仍然有效?
正如 CompileStatic
文档所说:
will actually make sure that the methods which are inferred as being
called will effectively be called at runtime. This annotation turns
the Groovy compiler into a static compiler, where all method calls are
resolved at compile time and the generated bytecode makes sure that
this happens
因此在静态编译中搜索了一个带Map参数的构造函数“在编译时解析它”,但没有找到,因此有一个编译错误:
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()
method of Groovy's GDK java.util.Map
,因此即使在静态编译中也能在运行时解决:
Coerces this map to the given type, using the map's keys as the public
method names, and values as the implementation. Typically the value
would be a closure which behaves like the method implementation.
Groovy 实际上 而不是 给你一个 "Map-Constructor"。建设者
在您的 class 中是您写下的内容。如果有 none (就像你的情况),
然后是默认的c'tor。
但是,如果您使用所谓的 map c'tor(或者更确切地说,将其称为
"object construction by map")? groovy的一般做法是这样的:
- 使用默认的 c'tor 创建一个新对象(这就是为什么
如果只有例如,按地图构建不再有效
SomeClass(Long id, String name)
)
- 然后使用传递下来的映射并将所有值应用到属性。
如果你反汇编你的代码(使用 @CompileDynamic
(默认值))你会看到,
施工由 CallSite.callConstructor(Object,Object)
处理,
归结为这个 code area.
现在引入这个地图构建的版本,比较熟悉
对于常规 groovyist:
SomeClass someClass3 = new SomeClass(id: 42L, name: "Douglas")
.
使用动态版本的代码,反汇编看起来确实如此
很像你的地图代码。 Groovy 从参数创建一个映射并且
将它发送到 callConstructor
- 所以这实际上是相同的代码路径
采取(减去隐式地图创建)。
暂时忽略 "cast-case",因为它对于静态和
动态:它将被发送到 ScriptBytecodeAdapter.asType
基本上
在任何情况下都为您提供动态行为。
现在 @CompileStatic
案例:如您所见,您的电话
c'tor 的显式映射不再有效。这是因为,
首先从来没有明确的 "map-c'tor" 。 class 还在
只有默认的 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
我是第一次使用 @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
仍然有效?
正如 CompileStatic
文档所说:
will actually make sure that the methods which are inferred as being called will effectively be called at runtime. This annotation turns the Groovy compiler into a static compiler, where all method calls are resolved at compile time and the generated bytecode makes sure that this happens
因此在静态编译中搜索了一个带Map参数的构造函数“在编译时解析它”,但没有找到,因此有一个编译错误:
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()
method of Groovy's GDK java.util.Map
,因此即使在静态编译中也能在运行时解决:
Coerces this map to the given type, using the map's keys as the public method names, and values as the implementation. Typically the value would be a closure which behaves like the method implementation.
Groovy 实际上 而不是 给你一个 "Map-Constructor"。建设者 在您的 class 中是您写下的内容。如果有 none (就像你的情况), 然后是默认的c'tor。
但是,如果您使用所谓的 map c'tor(或者更确切地说,将其称为 "object construction by map")? groovy的一般做法是这样的:
- 使用默认的 c'tor 创建一个新对象(这就是为什么
如果只有例如,按地图构建不再有效
SomeClass(Long id, String name)
) - 然后使用传递下来的映射并将所有值应用到属性。
如果你反汇编你的代码(使用 @CompileDynamic
(默认值))你会看到,
施工由 CallSite.callConstructor(Object,Object)
处理,
归结为这个 code area.
现在引入这个地图构建的版本,比较熟悉
对于常规 groovyist:
SomeClass someClass3 = new SomeClass(id: 42L, name: "Douglas")
.
使用动态版本的代码,反汇编看起来确实如此
很像你的地图代码。 Groovy 从参数创建一个映射并且
将它发送到 callConstructor
- 所以这实际上是相同的代码路径
采取(减去隐式地图创建)。
暂时忽略 "cast-case",因为它对于静态和
动态:它将被发送到 ScriptBytecodeAdapter.asType
基本上
在任何情况下都为您提供动态行为。
现在 @CompileStatic
案例:如您所见,您的电话
c'tor 的显式映射不再有效。这是因为,
首先从来没有明确的 "map-c'tor" 。 class 还在
只有默认的 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