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

前端 未结 8 456
温柔的废话
温柔的废话 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条回答
  •  独厮守ぢ
    2020-12-13 01:08

    I'd agree with previous answers/comments that say there isn't a way to do exactly what you want "using the standard JDK feature set." Thus, you are going to have to do some code generation, although it won't necessarily require changes to the build system. Since you ask:

    ... If not, what would it take to build one?

    ... For a simple case, not too much, I think. Suppose I put my primitive operations in a util class:

    public class NumberUtils {
    
        // @PrimitiveMethodsStart
        /** Find maximum of int inputs */
        public static int max(int a, int b) {
            return (a > b) ? a : b;
        }
    
        /** Sum the int inputs */
        public static int sum(int a, int b) {
            return a + b;
        }
        // @PrimitiveMethodsEnd
    
        // @GeneratedPrimitiveMethodsStart - Do not edit below
        // @GeneratedPrimitiveMethodsEnd
    }
    

    Then I can write a simple processor in less than 30 lines as follows:

    public class PrimitiveMethodProcessor {
        private static final String PRIMITIVE_METHODS_START = "@PrimitiveMethodsStart";
        private static final String PRIMITIVE_METHODS_END = "@PrimitiveMethodsEnd";
        private static final String GENERATED_PRIMITIVE_METHODS_START = "@GeneratedPrimitiveMethodsStart";
        private static final String GENERATED_PRIMITIVE_METHODS_END = "@GeneratedPrimitiveMethodsEnd";
    
        public static void main(String[] args) throws Exception {
            String fileName = args[0];
            BufferedReader inputStream = new BufferedReader(new FileReader(fileName));
            PrintWriter outputStream = null;
            StringBuilder outputContents = new StringBuilder();
            StringBuilder methodsToCopy = new StringBuilder();
            boolean inPrimitiveMethodsSection = false; 
            boolean inGeneratedPrimitiveMethodsSection = false; 
            try {
                for (String line;(line = inputStream.readLine()) != null;) {
                    if(line.contains(PRIMITIVE_METHODS_END)) inPrimitiveMethodsSection = false;
                    if(inPrimitiveMethodsSection)methodsToCopy.append(line).append('\n');
                    if(line.contains(PRIMITIVE_METHODS_START)) inPrimitiveMethodsSection = true;
                    if(line.contains(GENERATED_PRIMITIVE_METHODS_END)) inGeneratedPrimitiveMethodsSection = false;
                    if(!inGeneratedPrimitiveMethodsSection)outputContents.append(line).append('\n');
                    if(line.contains(GENERATED_PRIMITIVE_METHODS_START)) {
                        inGeneratedPrimitiveMethodsSection = true;
                        String methods = methodsToCopy.toString();
                        for (String primative : new String[]{"long", "float", "double"}) {
                            outputContents.append(methods.replaceAll("int\\s", primative + " ")).append('\n');
                        }
                    }
                }
                outputStream = new PrintWriter(new FileWriter(fileName));
                outputStream.print(outputContents.toString());
            } finally {
                inputStream.close();
                if(outputStream!= null) outputStream.close();
            }
        }
    }
    

    This will fill the @GeneratedPrimitiveMethods section with long, float and double versions of the methods in the @PrimitiveMethods section.

        // @GeneratedPrimitiveMethodsStart - Do not edit below
        /** Find maximum of long inputs */
        public static long max(long a, long b) {
            return (a > b) ? a : b;
        }
        ...
    

    This is an intentionally a simple example, and I'm sure it doesn't cover all cases, but you get the point and can see how it could be extended e.g. to search multiple files or use normal annotations and detect method ends.

    Furthermore, whilst you could set this up as a step in your build system, I set this up to run as a builder before the Java builder in my eclipse project. Now whenever I edit the file and hit save; it's updated automatically, in place, in less than a quarter of a second. Thus, this becomes more of a editing tool, than a step in the build system.

    Just a thought...

提交回复
热议问题