Converting Integer to BigDecimal in Groovy

半世苍凉 提交于 2021-01-28 02:06:37

问题


Let's assume that we have a function in groovy that takes as a parameter BigDecimal:

 void func(BigDecimal bd) {...}

And calling it again in other class on groovy var.func(0)

This is working fine, but in java it will not compile at all. I know that there is a constructor in BigDecimal that will work for Integer, but what's the reason that this works in groovy and java complain about it? Is there something with converting def variables to something known in java?


回答1:


Groovy uses argument type coercion when you call a method with a value of a different than declared type. In case of BigDecimal argument type coercion Groovy uses BigDecimalCachedClass.coerceArgument(Object argument) method:

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;
}

When you pass an Integer as a parameter then if (argument instanceof Number) branch is satisfied and Groovy converts input Integer to its BigDecimal representation.

It does not work in Java, because Java is not dynamic language, so all types have to be resolved at compile time. Groovy supports dynamic types, so your final type can be resolved in the runtime.

@CompileStatic and @TypeChecked

Groovy allows you to turn of its dynamic features. If you annotate your class with @CompileStatic or @TypeChecked, then calling var.func(0) won't work anymore, because Groovy will not use its dynamic features anymore.

Consider following simple Groovy class:

class NumTest {

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

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

When you compile it with groovyc you will see a Java class like:

//
// 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);
    }
}

What's interesting is that calling test(20) is not a direct static method call, but this instead:

var1[0].callStatic(NumTest.class, Integer.valueOf(20));

But if we annotate our class with @CompileStatic, it won't compile anymore and we will have to replace test(20) with direct test(BigDecimal.valueOf(20)) call:

import groovy.transform.CompileStatic

@CompileStatic
class NumTest {

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

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

Compiling this class with groovyc generates completely different Java 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.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;
    }
}

Here you can see that there is a direct call to test(BigDecimal.valueOf((long)20)); method, so there is no type coercion and you have to pass a valid type in place.



来源:https://stackoverflow.com/questions/48296049/converting-integer-to-bigdecimal-in-groovy

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!