#groovy #primitive-types
#groovy #примитивные типы
Вопрос:
Это дает 127
double middle = 255 / 2
В то время как это дает 127,5
Double middle = 255 / 2
Между тем это также дает 127,5
double middle = (255 / 2) as double
Я знаю, что Groovy работает с BigDecimal по умолчанию, но для меня это огромная ошибка! Как это может быть?
Ответ №1:
На самом деле это не имеет ничего общего с BigDecimals
, а скорее с приведением типа от примитивного integer к примитивному double . Эта проблема вызвана компилятором Groovy и (скорее всего) неправильным байт-кодом, который он создает. Взгляните на следующее представление байт-кода первого случая. Следующий заводной код:
void ex1() {
double x = 255 / 2
println x
}
компилируется в байт-код, который может быть представлен как:
public void ex1() {
CallSite[] var1 = $getCallSiteArray();
double x = 0.0D;
if (BytecodeInterface8.isOrigInt() amp;amp; BytecodeInterface8.isOrigD() amp;amp; !__$stMC amp;amp; !BytecodeInterface8.disabledStandardMetaClass()) {
int var5 = 255 / 2;
x = (double)var5;
} else {
Object var4 = var1[5].call(255, 2);
x = DefaultTypeTransformation.doubleUnbox(var4);
}
var1[6].callCurrent(this, x);
}
Это показывает, что в этом случае невозможно получить 127.5
в результате, потому что результат 255 / 2
выражения сохраняется в переменной типа int
. Похоже, что это пример непоследовательного поведения, потому что вот как Double
выглядит байт-код используемого метода:
public void ex2() {
CallSite[] var1 = $getCallSiteArray();
Double x = null;
if (BytecodeInterface8.isOrigInt() amp;amp; !__$stMC amp;amp; !BytecodeInterface8.disabledStandardMetaClass()) {
Object var4 = var1[8].call(255, 2);
x = (Double)ScriptBytecodeAdapter.castToType(var4, Double.class);
} else {
Object var3 = var1[7].call(255, 2);
x = (Double)ScriptBytecodeAdapter.castToType(var3, Double.class);
}
var1[9].callCurrent(this, x);
}
Основная проблема с этим вариантом использования заключается в том, что добавление @TypeChecked
не мешает вам совершать эту ошибку — компиляция проходит, и возвращается неверный результат. Однако, когда мы добавляем @TypeChecked
аннотацию к методу, который использует Double
ошибку компиляции. Добавление @CompileStatic
решает проблему.
Я провел несколько тестов и могу подтвердить, что эта проблема существует в последних версиях 2.5.6, а также 3.0.0-alpha-4. Я создал отчет об ошибке в проекте Groovy JIRA. Спасибо, что нашли и сообщили о проблеме!
ОБНОВЛЕНИЕ: Java делает то же самое
Похоже, что это не ошибка Groovy — именно так работает Java. В Java вы можете сохранить результат деления двух целых чисел в переменной double, но вы не получите ничего, кроме приведения целого числа к double . С типом {{Double}} все по-другому с точки зрения синтаксиса, но довольно похоже с точки зрения байт-кода. С помощью {{Double}} вам необходимо явно привести хотя бы одну часть уравнения к типу {{double}}, что приводит к байт-коду, который преобразует оба целых числа в {{double}} . Рассмотрим следующий пример на Java:
final class IntDivEx {
static double div(int a, int b) {
return a / b;
}
static Double div2(int a, int b) {
return a / (double) b;
}
public static void main(String[] args) {
System.out.println(div(255,2));
System.out.println(div2(255,2));
}
}
Когда вы запускаете его, вы получаете:
127.0
127.5
Теперь, если вы посмотрите на байт-код, который он создает, вы увидите что-то вроде этого:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
final class IntDivEx {
IntDivEx() {
}
static double div(int a, int b) {
return (double)(a / b);
}
static Double div2(int a, int b) {
return (double)a / (double)b;
}
public static void main(String[] args) {
System.out.println(div(255, 2));
System.out.println(div2(255, 2));
}
}
Единственное различие (с точки зрения синтаксиса) между Groovy и Java заключается в том, что Groovy позволяет неявно приводить целое число к Double
, и именно поэтому
Double x = 255 / 2
является правильным утверждением в Groovy, в то время как Java в этом случае завершается ошибкой во время компиляции со следующей ошибкой:
Error:(10, 18) java: incompatible types: int cannot be converted to java.lang.Double
Вот почему в Java вам нужно использовать приведение при назначении из целого числа в Double
.