基元类型导致的代码重复:如何避免疯狂?

2022-09-01 05:20:51

在我的一个Java项目中,由于Java处理(不是)原语的方式,我被代码重复所困扰。在不得不再次手动将相同的更改复制到四个不同的位置(,,,)之后,第三次,我一次又地非常接近(?)捕捉。intlongfloatdouble

以各种形式,这个问题在StackOverflow上不时被提出:

共识似乎趋同于两种可能的替代方案:

  • 使用某种代码生成器。
  • 你能做些什么?C'est la vie!

好吧,第二个解决方案是我现在正在做的事情,它对我的理智来说正在慢慢变得危险,就像众所周知的酷刑技术一样。

自从这些问题被问到以来已经过去了两年,Java 7出现了。因此,我希望找到一个更简单和/或更标准的解决办法。

  • 在这种情况下,Java 7是否有任何变化可以缓解压力?我在压缩的更改摘要中找不到任何内容,但也许在某个地方有一些晦涩难懂的新功能?

  • 虽然源代码生成是一种替代方法,但我更喜欢使用标准 JDK 功能集支持的解决方案。当然,使用cpp或其他代码生成器可以工作,但它会增加更多的依赖项,并且需要对构建系统进行更改。

    JDK似乎支持的唯一代码生成系统是通过注释机制。我设想一个处理器可以像这样扩展源代码:

    @Primitives({ "int", "long", "float", "double" })
    @PrimitiveVariable
    int max(@PrimitiveVariable int a, @PrimitiveVariable int b) {
        return (a > b)?a:b;
    }
    

    理想的输出文件将包含此方法的四个请求变体,最好是与关联的Javadoc注释e.t.c。是否有某个地方的注释处理器来处理这种情况?如果没有,建造一个需要什么?

  • 也许最近出现了其他一些技巧?

编辑:

重要提示:除非我有理由,否则我不会使用基元类型。即使是现在,在某些应用程序中使用盒装类型也会对性能和内存产生非常实际的影响。

编辑2:

用作示例允许使用在所有数字框类型中可用的方法。这有点棘手:max()compareTo()

int sum(int a, int b) {
    return a + b;
}

如何才能为所有数字盒装类型支持此方法,而无需实际编写六到七次?


答案 1

我倾向于使用“超级类型”,或者如果我仍然想要一个基元。性能通常非常接近,它避免了产生很多变化。顺便说一句:无论如何,64位JVM中的寄存器都将是64位。longdouble


答案 2

你为什么挂在原语上?包装器非常轻量级,自动装箱和泛型完成其余的工作:

public static <T extends Number & Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

这一切都可以正确编译和运行:

public static void main(String[] args) {
    int i = max(1, 3);
    long l = max(6,7);
    float f = max(5f, 4f);
    double d = max(2d, 4d);
    byte b = max((byte)1, (byte)2);
    short s = max((short)1, (short)2);
}

编辑

OP 已经询问了 一个通用的、自动装箱的解决方案,现在就在这里。sum()

public static <T extends Number> T sum(T... numbers) throws Exception {
    double total = 0;
    for (Number number : numbers) {
        total += number.doubleValue();
    }
    if (numbers[0] instanceof Float || numbers[0] instanceof Double) {
        return (T) numbers[0].getClass().getConstructor(String.class).newInstance(total + "");
    }
    return (T) numbers[0].getClass().getConstructor(String.class).newInstance((total + "").split("\\.")[0]);
}

它有点蹩脚,但不像做一大串并委托给一个完全类型化的方法那么蹩脚。这是必需的,因为虽然所有都有一个构造函数,但除了并且只能解析一个整数(没有小数点);尽管总数将是一个整数,但在将其发送到这些其他类型的构造函数之前,我们必须从 中删除小数点。instanceofinstanceofNumbersStringNumbersFloatDoubleDouble.toString()