Code duplication caused by primitive types: How to avoid insanity?

前端 未结 8 424
温柔的废话
温柔的废话 2020-12-13 00:44

In one of my Java projects I am plagued by code repetition due to the way Java handles (not) primitives. After having to manually copy the same change to four different loca

相关标签:
8条回答
  • Heh. Why not get sneaky? With reflection, you can pull the annotations for a method (annotations similar to the example you've posted). You can then use reflection to get the member names, and put in the appropriate types... In a system.out.println statement.

    You would run this once, or each time you modded the class. The output could then be copy-pasted in. This would probably save you significant time, and not be too hard to develop.

    Hm ,as for the contents of the methods... I mean, if all your methods are trivial, you could hard code the style (ie if methodName.equals("max") print return a>b:a:b etc. Where methodName is determined via reflection), or you could, ummmmm... Hm. I'm imagining the contents can be easily copy pasted, but that just seems more work.

    Oh! Whty not make another annotation called " contents ", give it a string value of the method contents, add that to the member, and now you can print out the contents too.

    In the very least, the time spent coding up this helper, even if about as long as doing the tedious work, well, it would be more interesting, riiiight?

    0 讨论(0)
  • 2020-12-13 00:46

    Why are you hung up on primitives? The wrappers are extremely lightweight and auto-boxing and generics does the rest:

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

    This all compiles and runs correctly:

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

    Edited

    OP has asked about a generic, auto-boxed solution for sum(), and will here it is.

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

    It's a little lame, but not as lame as doing a large series of instanceof and delegating to a fully typed method. The instanceof is required because while all Numbers have a String constructor, Numbers other than Float and Double can only parse a whole number (no decimal point); although the total will be a whole number, we must remove the decimal point from the Double.toString() before sending it into the constructor for these other types.

    0 讨论(0)
  • 2020-12-13 00:52

    If the extraordinary verbosity of Java is getting to you, look into some of the new, higher-level languages which run on the JVM and can interoperate with Java, like Clojure, JRuby, Scala, and so on. Your out-of-control primitive repetition will become a non-issue. But the benefits will go much further than that -- there are all kinds of ways which the languages just mentioned allow you to get more done with less detailed, repetitive, error-prone code (as compared to Java).

    If performance is a problem, you can drop back into Java for the performance-critical bits (using primitive types). But you might be surprised at how often you can still get a good level of performance in the higher-level language.

    I personally use both JRuby and Clojure; if you are coming from a Java/C/C#/C++ background, both have the potential to change the way you think about programming.

    0 讨论(0)
  • 2020-12-13 00:55

    Does Java 7 have any changes that might ease the strain in such cases?

    No.

    Is there somewhere an annotation processor to handle this case?

    Not that I am aware of.

    If not, what would it take to build one?

    Time, or money. :-)

    This seems to me like a problem-space where it would be difficult to come up with a general solution that works well ... beyond trivial cases. Conventional source code generation or a (textual) preprocessor seems more promising to me. (I'm not an Annotation processor expert though.)

    0 讨论(0)
  • 2020-12-13 01:01

    From the performance point of view (I make a lot of CPU-bound algorithms too), I use my own boxings that are not immutable. This allows using mutable numbers in sets like ArrayList and HashMap to work with high performance.

    It takes one long preparation step to make all the primitive containers with their repetitive code, and then you just use them. As I also deal with 2-dimensional, 3-dimensional etc values, I also created those for myself. The choice is yours.

    like:
    Vector1i - 1 integer, replaces Integer
    Vector2i - 2 integer, replaces Point and Dimension
    Vector2d - 2 doubles, replaces Point2D.Double
    Vector4i - 4 integers, could replace Rectangle
    Vector2f - 2-dimensional float vector
    Vector3f - 3-dimensional float vector
    ...etc...
    All of them represent a generalized 'vector' in mathematics, hence the name for all these primitives.

    One downside is that you cannot do a+b, you have make methods like a.add(b), and for a=a+b I chose to name the methods like a.addSelf(b). If this bothers you, take a look at Ceylon, which I discovered very recently. It's a layer on top of Java (JVM/Eclispe compatbile) created especially to address it's limitations (like operator overloading).

    One other thing, watch out when using these classes as a key in a Map, as sorting/hashing/comparing will go haywire when the value changes.

    0 讨论(0)
  • 2020-12-13 01:05

    I tend to use a "super type" like long or double if I still want a primitive. The performance is usually very close and it avoids creating lots of variations. BTW: registers in a 64-bit JVM will all be 64-bit anyway.

    0 讨论(0)
提交回复
热议问题