原始类型引起的代码重复:如何避免精神错乱?

thk*_*ala 37 java primitive code-duplication

在我的一个Java项目中,由于Java处理(而非)原语的方式,我受到代码重复的困扰.具有相同的变化手工复制到四个不同的位置(之后int,long,float,double)再次,对于第三次的时候,再次我来到非常接近(?)来抢购.

在各种形式中,这个问题已经在StackOverflow上提出了:

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

  • 使用某种代码生成器.
  • 你能做什么?这就是生活!

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

自从提出这些问题并且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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    理想的输出文件将包含此方法的四个请求变体,最好使用相关的Javadoc注释等.是否有某处注释处理器来处理这种情况?如果没有,构建一个会怎样?

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

编辑:

一个重要的注意事项:除非我有理由,否则我不会使用原始类型.即使是现在,在某些应用程序中使用盒装类型也会产生非常真实的性能和内存影响.

编辑2:

使用max()作为示例允许使用compareTo()所有数字盒装类型中可用的方法.这有点棘手:

int sum(int a, int b) {
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)

怎么可以为所有数字盒装类型支持这种方法而不实际写入六到七次?

Pet*_*rey 18

我倾向于使用"超类型" long或者double如果我仍然想要一个基元.性能通常非常接近,避免产生大量变化.BTW:无论如何,64位JVM中的寄存器都是64位的.

  • +1一个有趣的观点:除非它与数组有关,具有特定于`byte`,`char`或`short`的代码可能是无用的 - 根据JLS,对于大多数操作,所有这些值都必须被提升为`int` ,所以字节码与`int`的代码相同... (2认同)
  • 通常情况下,下溢或溢出通常是不合需要的,但通常可以在最后强制转换为适当的类型. (2认同)

Boh*_*ian 15

你为什么要挂在原始人身上?包装器非常轻巧,自动装箱,其余的是泛型:

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

这一切都编译并正确运行:

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);
}
Run Code Online (Sandbox Code Playgroud)

编辑

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]);
}
Run Code Online (Sandbox Code Playgroud)

它有点蹩脚,但并不像执行大量系列instanceof并委托给完全类型的方法那样蹩脚.这instanceof是必需的,因为虽然所有Numbers都有一个String构造函数,Numbers除了Float并且Double只能解析一个整数(没有小数点); 虽然总数将是一个整数,但我们必须从Double.toString()将它们发送到这些其他类型的构造函数之前删除小数点.

  • 性能,性能,性能.有一次我使用盒装类型,我的算法慢了3-4倍 - 然后我的内存耗尽...... (15认同)
  • @Bohemian:对于Oracle服务器VM 1.7.0_02,您的泛型方法运行时间比等效的int max(int,int)实现长7倍,比long max(long,long)长12倍.即使绝对差异很小,相对差异也可能非常相关.算术运算:甚至可以用Number实例做加法和减法等简单的事情吗? (3认同)

Ste*_*n C 5

Java 7是否有任何可能在这种情况下缓解压力的变化?

没有.

有没有一个注释处理器来处理这种情况?

不是我知道的.

如果没有,构建一个会怎样?

时间还是金钱.:-)

在我看来,这似乎是一个问题空间,很难找到一个运行良好的通用解决方案......超越琐碎的案例.传统的源代码生成或(文本)预处理器似乎对我更有希望.(虽然我不是Annotation处理器专家.)