在Groovy中将整数转换为BigDecimal

4
假设我们在groovy中有一个函数,它的参数是BigDecimal:
 void func(BigDecimal bd) {...}

在Groovy中调用var.func(0)没有问题。

但在Java中,这段代码根本无法编译。我知道BigDecimal有一个适用于Integer的构造函数,但为什么在Groovy中可以工作而Java却会报错呢?是因为将def变量转换为Java中已知类型的原因吗?


在Groovy中,def类似于Java中的Object,但是这个问题对我来说不太清楚... - daggett
@daggett请检查标记的答案。解释非常好 :) - Georgi Stoyanov
1个回答

2

Groovy在调用不同于声明类型的值的方法时使用参数类型强制转换。对于BigDecimal参数类型强制转换,Groovy使用BigDecimalCachedClass.coerceArgument(Object argument)方法:

public Object coerceArgument(Object argument) {
    if (argument instanceof BigDecimal) {
        return argument;
    } else if (argument instanceof Long) {
        return new BigDecimal((Long) argument);
    } else if (argument instanceof BigInteger) {
        return new BigDecimal((BigInteger) argument);
    }

    if (argument instanceof Number) {
        return new BigDecimal(((Number) argument).doubleValue());
    }
    return argument;
}

当您将一个整数作为参数传递时,if(argument instanceof Number)分支被满足,并且Groovy将输入的整数转换为其BigDecimal表示形式。在Java中这不起作用,因为Java不是动态语言,所以所有类型必须在编译时解析。Groovy支持动态类型,因此您的最终类型可以在运行时解析。 @CompileStatic@TypeChecked Groovy允许您关闭其动态功能。如果您对类进行注释@CompileStatic@TypeChecked,则调用var.func(0)将不再起作用,因为Groovy将不再使用其动态特性。
考虑以下简单的Groovy类:
class NumTest {

    static void main(String[] args) {
        test(20)
    }

    static void test(BigDecimal number) {
        println number
    }
}

当你使用groovyc编译时,你会看到一个类似于Java的类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.math.BigDecimal;
import org.codehaus.groovy.runtime.callsite.CallSite;

public class NumTest implements GroovyObject {
    public NumTest() {
        CallSite[] var1 = $getCallSiteArray();
        MetaClass var2 = this.$getStaticMetaClass();
        this.metaClass = var2;
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callStatic(NumTest.class, Integer.valueOf(20));
    }

    public static void test(BigDecimal number) {
        CallSite[] var1 = $getCallSiteArray();
        var1[1].callStatic(NumTest.class, number);
    }
}

有趣的是,调用test(20)不是直接的静态方法调用,而是这样的:
var1[0].callStatic(NumTest.class, Integer.valueOf(20));

但是如果我们在类上注解@CompileStatic,它将不再编译,并且我们必须用直接调用test(BigDecimal.valueOf(20))替换test(20)

import groovy.transform.CompileStatic

@CompileStatic
class NumTest {

    static void main(String[] args) {
        test(BigDecimal.valueOf(20))
    }

    static void test(BigDecimal number) {
        println number
    }
}

使用groovyc编译此类会生成完全不同的Java类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.math.BigDecimal;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;

public class NumTest implements GroovyObject {
    public NumTest() {
        MetaClass var1 = this.$getStaticMetaClass();
        this.metaClass = var1;
    }

    public static void main(String... args) {
        test(BigDecimal.valueOf((long)20));
        Object var10000 = null;
    }

    public static void test(BigDecimal number) {
        DefaultGroovyMethods.println(NumTest.class, number);
        Object var10000 = null;
    }
}

您可以看到这里直接调用了test(BigDecimal.valueOf((long)20));方法,因此没有类型强制转换,您必须传递一个有效的类型。


谢谢你的好解释 :) 你知道我可以在哪里查看一些“神奇的Groovy”是如何实现的,比如使用def,我认为这有点类似? - Georgi Stoyanov
@GeorgiStoyanov 有一个关于 def vs. Object 的问题,我认为你可以在这里找到一些有用的信息 https://dev59.com/DFkR5IYBdhLWcg3w0AKy#40827126 - Szymon Stepniak
有人知道在Groovy文档中哪里记录了将方法参数强制转换为BigDecimal的内容吗? - eekboom
BigDecimalCachedClass 实例是在 ClassInfo.java 中创建的。这是否意味着无法对用户类使用 coerceArgument - stilgar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接