Groovy';s@CompileStatic和映射构造函数

Groovy';s@CompileStatic和映射构造函数,groovy,Groovy,我第一次使用@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 fi

我第一次使用
@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:尚未设置构造函数调用表达式的目标构造函数

如果删除了
@CompileStatic
,则两个构造函数都能正常工作

谁能解释一下为什么
newsomeclass(map)
不能用
@CompileStatic
编译?还有一个可能的补充,为什么
映射为SomeClass
仍然有效?

如文档所述:

将实际确保推断为 调用的将在运行时有效调用。此注释将 将Groovy编译器转换为静态编译器,其中所有方法调用都是 在编译时解析,生成的字节码确保 这种情况会发生

因此,在静态编译中搜索带有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
    }
}
如果你想挖得更深,你可以检查一下

关于线路

SomeClass someClass2 = map as SomeClass
您在那里使用的是方法,因此即使在静态编译中,也会在运行时解决:

将此映射强制为给定类型,使用映射的键作为公共 方法名称和值作为实现。通常是值 将是一个行为类似于方法实现的闭包


Groovy实际上没有给你一个“映射构造函数”。建设者 你在课堂上写的是什么。如果没有(如您的情况), 然后是默认的c'tor

但是,如果你使用所谓的map c'tor(或者更确切地说,称之为map c'tor),会发生什么呢 “通过映射构造对象”)?groovy的一般方法如下:

  • 使用默认的c'tor创建一个新对象(这就是为什么 根据地图进行施工已不再有效,如果只有。
    SomeClass(长id、字符串名)
  • 然后使用传递的映射并将所有值应用于属性
如果您(使用
@CompileDynamic
(默认值))禁用代码,您会看到 构造由
CallSite.callConstructor(Object,Object)
,, 归结起来就是这个

现在通过地图引入这个构造的版本,这是更熟悉的 对于普通的Groovy开发者:
SomeClass-someClass3=新的SomeClass(id:42L,名称:“道格拉斯”)

对于代码的动态版本,它的反汇编实际上看起来很简单 很喜欢你的地图代码。Groovy从参数和 将其发送到
callConstructor
——因此这实际上是相同的代码路径 采取(减去隐式贴图创建)

现在请忽略“cast case”,因为它实际上对于静态和静态都是相同的 动态:它将被发送到
ScriptBytecodeAdapter.asType
,基本上 在任何情况下都能为您提供动态行为

现在是
@CompileStatic
案例:如您所见,您的呼叫 c'tor的显式映射不再有效。这是因为 首先,从来没有一个明确的“map-c'tor”。全班同学还在上课 只有默认的c'tor和静态编译
groovyc
现在才可以 处理存在的事物(如果在本例中没有,则处理不存在的事物)

那新的SomeClass(id:42L,名字:“Douglas”)呢?这仍然有效 使用静态编译!这样做的原因是,
groovyc
为你。如您所见,这可以归结为
defo=newsomeclass();
o、 setId(42);o、 setName('Douglas')

new#2//class SomeClass
重复
调用特殊的#53//方法“”:()V
阿斯托雷2
ldc2_w#54/长42l
dup2
奥斯托雷3
aload_2
lload_3
invokestatic#45//方法java/lang/Long.valueOf:(J)Ljava/lang/Long;
invokevirtual#59//方法setId:(Ljava/lang/Long;)V
空的
流行音乐
pop2
ldc#61//String Douglas
重复
阿斯托尔5号
aload_2
阿洛德5号
invokevirtual#65//方法setName:(Ljava/lang/String;)V

我认为答案比公认的好。谢谢
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