Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 locations (int, long, float, double) again, for the third time, again and again I came really close (?) to snapping.

In various forms, this issue has been brought up now and then on StackOverflow:

  • Managing highly repetitive code and documentation in Java
  • How to avoid repetition when working with primitive types?
  • Passing dynamic list of primitives to a Java method

The consensus seemed to converge to two possible alternatives:

  • Use some sort of code generator.
  • What can you do? C'est la vie!

Well, the second solution is what I am doing now and it is slowly becoming dangerous for my sanity, much like the well known torture technique.

Two years have passed since these questions were asked and Java 7 came along. I am, therefore, hopeful for an easier and/or more standard solution.

  • Does Java 7 have any changes that might ease the strain in such cases? I could not find anything in the condensed change summaries, but perhaps there is some obscure new feature somewhere?

  • While source code generation is an alternative, I'd prefer a solution supported using the standard JDK feature set. Sure, using cpp or another code generator would work, but it adds more dependencies and requires changes to the build system.

    The only code generation system of sorts that seems to be supported by the JDK is via the annotations mechanism. I envision a processor that would expand source code like this:

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

    The ideal output file would contain the four requested variations of this method, preferrably with associated Javadoc comments e.t.c. Is there somewhere an annotation processor to handle this case? If not, what would it take to build one?

  • Perhaps some other trick that has popped up recently?

EDIT:

An important note: I would not be using primitive types unless I had a reason. Even now there is a very real performance and memory impact by the use of boxed types in some applications.

EDIT 2:

Using max() as an example allows the use of the compareTo() method that is available in all numeric boxed types. This is a bit trickier:

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

How could one go about supporting this method for all numeric boxed types without actually writing it six or seven times?

like image 407
thkala Avatar asked Mar 12 '12 11:03

thkala


People also ask

How do you avoid code duplication?

Don't Repeat Yourself (DRY): Using DRY or Do not Repeat Yourself principle, you make sure that you stay away from duplicate code as often as you can. Rather you replace the duplicate code with abstractions or use data normalization. To reduce duplicity in a function, one can use loops and trees.

Why do we avoid code duplication?

Having to change the same code multiple times harms your cycle time. If you have to apply a change in multiple places, then implementing that change will take longer. If the duplication is pervasive enough, it'll lead to a decreased delivery speed.

Is duplicated code always bad?

Duplication is bad, but… It isn't a question of whether you'll remember: it's a question of when you'll forget.” Which makes perfect sense. It's time well spent when you try to make your code streamlined and readable. You'll end up with a cleaner, easier-to-maintain, and more extensible code base as a result.


2 Answers

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.

like image 147
Peter Lawrey Avatar answered Oct 02 '22 12:10

Peter Lawrey


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.

like image 40
Bohemian Avatar answered Oct 02 '22 12:10

Bohemian